暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL索引

Odyssey冲鸭 2021-05-18
213

基于主键实现的索引

针对主键的索引其实就是主键目录,将数据页的页码和这个数据页里的最小主键放在一起,组成一个目录

比如:

  1. 最小主键=1 页码=2

  2. 最小主键=14 页码=8

  3. 最小主键=30 页码=10

现在如果我们要找id=3的数据,那么按照主键索引就会跟每个数据页的最小主键进行比较,1 < 3 < 14.那么就可以知道id=3的数据就在数据页2里面。

如果有很多的数据页,那么主键索引目录也会有多条记录,可以采用二分法来找到要找的数据在哪个数据页里

B+树实现的索引

当表里的数据很多的时候,主键索引目录也会有很多数据,所以mysql也是采用索引页来存储的,假如我们要查找id=49的数据,我们需要先知道到哪个索引页上去查询对应的数据页信息。

        page = 35
  / \
page=22 page = 40
/ \ / \
p=15 p=25 p=38 p=42
复制

查询时先从根节点开始查询,通过二分法查询到对应的数据页,在进入数据页找到id=49的数据即可

聚簇索引也就是主键索引,聚簇索引B+树的叶子节点其实就是数据页,叶子节点的节点都是通过指针相连组成了双向链表,所以如果要进行范围查询,就可以很高效的查询得到想要的数据

在InnoDB引擎里,对数据进行增删改的时候,就是对聚簇索引里的数据页进行增删改,如果数据页进行分裂了,它会调整各个数据页内部的数据,保证每个数据页的数据都是有序的,并且下一个数据页的所有主键值大于上一个数据页的最大主键。

同时在页分裂的时候,也会维护上层的索引页。随着数据页越来越多,一个索引页放不下了,就又会开辟新的索引页,同时搞出一个上层的索引页,这个索引页里记录的就是下层的索引页的页号和最小主键值。

普通索引

也叫二级索引,如idx(name,age),这棵B+树的叶子节点仅包含索引字段name,age及主键id。如果要查询除了这三个字段以外的其他字段,那么还需要回表通过主键到聚簇索引上查找其他字段,所以在写sql时需要注意,仅返回需要的字段就好。避免select * 操作

如上的联合索引,在存储的时候,如果name一样,那么再按照age进行排序。都是有序排列的。

索引是越多越好吗

当然不是,索引树也是需要占磁盘空间的,另一方面,在更=增删改数据的时候也需要维护索引树。因此索引也不是越多越好,一般建两三个合适的即可

索引使用基本规则

  • 最左侧列匹配 ---只要where条件中有最左侧列的字段即可

  • 最左前缀匹配   ---模糊搜索时,只有%号在后边才可以

  • 范围查找规则   ---第一个范围查找是可以使用索引的,因为是按照顺序排列的,但是后面的就不行了

    如: 建立索引idx(class_name,stu_name,sub_name),select * from table where class_name=1 and stu_name > "" and sub_name < "", 这里class_name和 stu_name 都可以用到索引,sub_name就不行了

所以综上所述,一般我们如果写SQL语句,都是用联合索引的最左侧的多个字段来进行等值匹配+范围搜索,或者是基于最左侧的部分字段来进行最左前缀模糊匹配,或者基于最左侧字段来进行范围搜索,这就要写符合规则的SQL语句,才能用上我们建立好的联合索引!

SQL 里order by排序的时候如何才能使用索引?

如果建立了索引idx(col1,col2,col3).本身索引树就是按照字段大小进行排序的,所以在查找进行排序的时候,需要排序的字段的排序最后一致,都是asc,或者都是desc

如果都是升序排列,直接就从索引树里最小的开始读取一定条数就可以了,要是都是降序排列,就是从索引树里最大的数据开始读取一定的条数就可以了,但是你不能order by语句里有的字段升序有的字段降序,那是不能用索引的。

另外,要是你order by语句里有的字段不在联合索引里,或者是你对order by语句里的字段用了复杂的函数,这些也不能使用索引去进行排序了。

SQL里进行group by进行分组的时候如何才能使用索引?

索引树中都是按照我们索引指定的字段进行排序的,字段相同的数据都是放在一起的,如果按照索引进行分组,那肯定也是相当快的。所以group by也是,最好按照索引从最左列开始的顺序一致进行。充分利用索引树里顺序排列的性质

回表及覆盖索引是什么?

回表:普通索引叶子节点包含的是索引列的值和主键,当要查询的字段在索引树上没有的时候,就需要通过主键回查聚簇索引拿到相应的字段,这就是回表。这样查询性能也是很受影响的。当回表次数过多的时候可能就直接全表扫描了,所以通常需要注意以下两点:

  • 通常在写sql的时候避免select *,仅查询需要的字段即可。

  • 就算要回表,也尽量避免回表的次数,通过where条件和limit来限定下回表次数。

覆盖索引:覆盖索引不是索引,而是一种基于索引查询的方式,如果索引上包含需要查询的字段了,那么就不需要回表再通过聚簇索引去找其他字段了,直接扫描联合索引树就可以了

如何设计索引?

  • 通常在设计完表结构之后,先不急着建立索引,当我们通过mybatis写完业务sql之后,当我们已经知道要针对哪些字段来进行查询的时候再去设计索引

  • 针对where条件,order by,group by等条件来设计,设计的联合索引尽量包含where条件,order by条件,group by条件里的字段

  • 主键是自增的,不要使用uuid。因为增删改会更新索引树

  • 最重要一点,符合最左原则

  • 比如你有一个联合索引是idx(a,b,c),此时发现有三个SQL,包含了where a=? and b=?,order by a,b,group by a这些部分,那么此时where、order by、group by后续跟的字段都是联合索引的最左侧开始的部分字段,这就可以了,说明你的每个SQL语句都会用上你的索引了。

  • 选择字段值分布大的列建立索引,像性别这种只有两种的就不适合建立索引,因为索引是二分查找,需要充分利用算法

  • 选择字段类型比较小的列建立索引,如果有varchar(255)这种,可以选择建立前缀索引,如idx(name(20),age)针对name字段的前20个字符建立索引,那么我们在用where name=xxx 进行搜索时会根据name字段的前20个字符来搜索,定位到前20个字符匹配到的数据后再回表到聚簇索引取完整的name字段即可

  • 查询时不要使用函数和反向条件,如where function(x) = xx或 where name != xxx,前面一直说索引树是顺序排列的,当搜索的时候,大于就往右边找,小于往左边找,你给他来个!= ,那它就不知道往哪边找了,所以就干脆全表扫描了


文章转载自Odyssey冲鸭,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论