写在开头
本文章适合了解mysql索引以及B+树的朋友观看
基础知识
- 1.一次IO,可以加载一个node(索引树的一个节点),具体可以参考这篇文章
- 2.mysql每次IO,读取的大小默认为16k,该值可以修改
- 3.当我们创建表时没有设置主键,mysql会选一列(该列的值为“唯一”)作为主键列,如果不存在“唯一列”,mysql会自动生成row_id列做主键。并且会根据该主键生成主键索引
- 4.只有主键索引的叶子节点保存了具体的行数据,普通索引的叶子节点保存的是id值。通过普通索引获取到id值后再经过主键索引查询到具体数据这一过程称之为“回表”
- 5.聚簇索引(主键索引):是一种数据的存储结构,指所有完整的用户记录都存放在这个聚簇索引的叶子节点处
- 6.一个表只能有一个聚簇索引(主键索引)
什么是回表
假设我现在有一个teacher表,主键为id,并且单独为name建立了一个索引
image.png
那么这张表的主键索引数据结构如下:
image.png
当我执行select * from teacher wehre id = 1的查询过程
- 1.where 后面跟的是id,有存在id的索引,根据索引进行查询
- 2.进行一次IO操作,将最B+树最顶部节点的索引数据读取到内存中
- 比对发现1<3,走左1指针
- 进行一次IO操作,将左一节点数据加载到内存
- 5.比对并找到id=1,返回数据(1,周老师)
teacher表name索引的数据结构如下,下图只是模拟了一下,排序是错误的:
image.png可以看到普通索引name的索引图中,叶子节点并不保存具体的行数据,只是保存了id
当我们执行select * from teacher where name="李老师”,通过name索引获取到的是一个id,如果要得到具体的行数据,还得通过id去主键索引中查询,这个过程就叫回表
回表带来的问题
当查询需要进行回表操作时,对磁盘的IO操作会增加,我们都知道磁盘的读写速度远慢于内存,增加的IO操作无疑会增加查询语句执行的时间。
如何避免回表(索引覆盖)
image.png我们再来看看普通索引的结构图,当我们查询的数据,在普通索的叶子节点就能获取到时,就不再需要执行回表的操作,也就是当我们执行以下查询语句时,是不需要进行回表操作的
- select id from teacher where name like "李%"
- select name from teacher where name like "李%"
- select id,name from teacher where name like "李%"
需要注意的是select * 是一定会进行回表操作的,所以我们写sql时要避免写select *
上面提到的避免回表操作的方法称之为“索引覆盖”
最左匹配
当我们创建一个组合索引(id,name)时,下面的查询语句时可以使用到该组合索引的?
- select id, name from teacher where id=1 and name = "李老师"
- select id,name from teacher where id=1
- select id,name from teacher where name="李老师"
- select id,name from teacher where name="李老师" and id=1
答:
1和2,where后面跟着的查询条件顺序符合最左匹配原则,可以走该组合索引。
4语句会被mysql的优化器优化成1,所以也会走该组合索引
模糊查询是否会走索引
select id from teacher where name like "李%",走name索引
select id from teacher where name like "%李",不走name索引
组合索引+范围查询的情况下会不会用到索引
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止使用索引
比如a = 1 and b = 2 and c > 3 and d = 4
如果建立(a,b,c,d)顺序的索引,d是用不到索引的,
如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整(建立索引时他们三个的顺序)
索引列不能参与计算,保持列“干净”,
以下的截图可以很直观的看到为什么不能让索引列参与计算,表里的数据只有6行
image.png image.png
网友评论