一、索引的基础认知
- 索引可以加快数据库的检索速度
- 表常进行增(insert)删(delete)改(update)操作,不建议建立索引,因为索引会降低增删改等维护操作的速度
- 索引占用物理空间和数据空间
- 索引具有最左匹配原则
- 索引分为:聚集索引和非聚集索引
- Mysql支持Hash索引和B+Tree索引
二、索引的学习要点
- 为什么使用索引可以加快数据库的检索速度?
- 为什么索引会降低增删改等维护操作的速度?
- 索引的最左匹配原则指的是什么?
- Hash索引和B+Tree索引有什么区别?
- 聚集索引和非聚集索引有什么区别?
三、索引内容学习
1. 索引的基础知识
Mysql的基本存储结构是页(所有的记录都存储在页里):
- 各个数据页可以组合成一个双向链表
- 每个数据页中的所有记录可以组成一个单向链表
- 每个数据页都会为存储在他里面的记录生成一个页目录,在我们通过主键查找某条记录的时候就可以在页目录中使用二分法快速定位到对应的槽,然后遍历该槽对应分组中的记录可以快速找到指定记录
- 以其他列作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录
比如,如果执行 select * from user where username = 'Java' 这样没有进行任何优化的sql语句,默认会这样做:
- 定位到记录所在的页:需要遍历双向链表,找到所在的页
- 从所在的页内中查找相应的记录:由于不是根据主键查询,只能遍历所在页的单链表
在数据量很大时,查询速度非常慢!
2.索引提高检索速度
索引建立了B+Tree树形结构,将无序的数据结构重组成有序:
3.索引降低增删改的速度
- B+Tree是一种平衡树,树的高度相对比较低,符合矮胖结构,左右两个子树的高度差值的绝对值不会超过1,并且所有两个子树也是平衡二叉树。
- 对B+Tree进行增删改操作会破坏他的原有平衡结构,为了维持树的平衡结构就需要将数据重新组合成平衡结构,这些额外的工作就会导致降低增删改等操作的速度。
4.哈希索引
- 除B+Tree之外常见的一种索引就是哈希索引
- 哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快
局限性:
- 哈希索引没办法利用索引完成排序
- 不支持最左匹配原则
- 哈希碰撞问题
- 不支持范围查询
5.innoDB支持Hash索引和B+Tree索引
6.聚集索引和非聚集索引的区别
简单来说:
- 聚集索引就是以主键创建的索引
- 非聚集索引就是以非主键创建的索引
区别:
- 聚集索引在叶子节点存储的是表中的数据
- 非聚集索引在叶子节点存储的是主键和索引列
- 使用非聚集索引查询出数据时,拿到叶子上的主键再去查到想要查找的数据(拿到主键再查找这个过程叫做回表)
非聚集索引在建立的时候也未必是单列的,可以多个列来创建索引
- 此时就涉及到了哪个列会走索引,哪个列不走索引的问题了(最左匹配原则-->后面有说)
- 创建多个单列(非聚集)索引的时候,会生成多个索引树(所以过多创建索引会占用磁盘空间)
在创建多列索引中也涉及到了一种特殊的索引 -- 覆盖索引
- 我们前面知道了,如果不是聚集索引,叶子节点存储的是主键+列值
- 最终还是要“回表”,也就是要通过主键再查找一次。这样就会比较慢
- 覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
比如说:
- 现在我创建了索引(username,age),在查询数据的时候:select username,age from user where username='Java' and age=20
- 很明显地知道,我们上边的查询是走索引的,并且,要查询出的列在叶子节点都存在!所以,就不用回表了
- 所以,能使用覆盖索引就尽量使用吧
7.索引最左匹配原则
- 索引可以简单如一个列(a),也可以复杂如多个列(a, b, c, d),即联合索引。
- 如果是联合索引,那么key也由多个列组成,同时,索引只能用于查找key是否存在(相等),遇到范围查询(>、<、between、like左匹配)等就不能进一步匹配了,后续退化为线性查找。
- 因此,列的排列顺序决定了可命中索引的列数。
例子:
- 如有索引(a, b, c, d),查询条件a = 1 and b = 2 and c > 3 and d = 4,则会在每个节点依次命中a、b、c,无法命中d。(很简单:索引命中只能是相等的情况,不能是范围匹配)
8.=、in自动优化顺序
- 不需要考虑=、in等的顺序,mysql会自动优化这些条件的顺序,以匹配尽可能多的索引列
- 如有索引(a, b, c, d),查询条件c > 3 and b = 2 and a = 1 and d < 4与a = 1 and c > 3 and b = 2 and d < 4等顺序都是可以的,MySQL会自动优化为a = 1 and b = 2 and c > 3 and d < 4,依次命中a、b、c
9.索引总结
上面谈的其实就是索引最基本的东西,要创建出好的索引要顾及到很多的方面:
- 最左前缀匹配原则。这是非常重要、非常重要、非常重要(重要的事情说三遍)的原则,MySQL会一直向右匹配直到遇到范围查询(>,<,BETWEEN,LIKE)就停止匹配。
- 尽量选择区分度高的列作为索引,区分度的公式是 COUNT(DISTINCT col) / COUNT(*)。表示字段不重复的比率,比率越大我们扫描的记录数就越少。
- 索引列不能参与计算,尽量保持列“干净”。比如,FROM_UNIXTIME(create_time) = '2016-06-06' 就不能使用索引,原因很简单,B+树中存储的都是数据表中的字段值,但是进行检索时,需要把所有元素都应用函数才能比较,显然这样的代价太大。所以语句要写成 : create_time = UNIX_TIMESTAMP('2016-06-06')。
- 尽可能的扩展索引,不要新建立索引。比如表中已经有了a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
- 单个多列组合索引和多个单列索引的检索查询效果不同,因为在执行SQL时,MySQL只能使用一个索引,会从多个单列索引中选择一个限制最为严格的索引。