MySQL索引
MySQL存储结构
MySQL的基本存储结构是页(记录都存在页里面),各个数据页可以组成一个双向链表,每个数据页都会为存储在它里面的记录生成一个页目录,每个数据页中的内容又可以组成一个单向链表。
主键查询
从第一个页开始,沿着双向链表对每一个页进行查找,每个数据页都会为存储在它里面的记录生成一个页目录,在通过主键查找某条记录的时候可以使用二分法快速定位到对应的页,然后遍历该页里面对应分组的记录就可以快速找到指定的记录
普通查询
当我们查询一个没有任何优化的sql语句时,会先遍历双向链表,定位到所在的页,由于不是根据主键查询,因此会从最小记录开始一条一条遍历页所在的单链表中的每条记录,然后对比每条记录是不是复合搜索条件
聚簇索引和非聚簇索引
聚簇索引
聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。当表有聚簇索引时,它的数据行实际上存放在索引的叶子页中,也就是 B+Tree的叶子节点上。因为数据行不能存在两个地方,所以一个表只能有一个聚簇索引。InnoDB存储引擎会自动的为我们创建聚簇索引。
- 使用记录主键值的大小进行记录和页的排序
- B+树的叶子节点存储的是完整的用户记录
非聚簇索引
非聚簇索引也是B-Tree数据结构,它的叶子节点包含了引用行的主键列。这意味着通过非聚簇索引查找行,存储引擎需要找到非聚簇索引的叶子节点获得对应的主键,然后根据这个主键值去聚簇索引中查找到对应的行。这里做了至少两次的 BTree 查找。
回表
当我们查询主键时会采用上述的B+树结构内容进行查询,如果是查询其他列,那么就会为相应的列也同样创建一个这样的B+树。
但是页内的记录是按照指定列的大小顺序排成一个单向链表。
各个存放用户记录的页也是根据页中记录的指定列大小顺序排成一个双向链表。
各个存放目录项的页也是根据页中记录的指定列大小顺序排成一个双向链表。
在这个B+树的叶子节点存储的并不是完整的用户记录,而只是指定列+主键这两个列的值,所以我们必须再根据主键值去聚簇索引中再查找一遍完整的用户记录,这个过程也被称为回表。
覆盖索引
mysql 可以使用索引直接来获取列的数据,这样就可以不再需要读取数据行,
比如:select id from order where user_id between 1 and 3
这时候只需要查ID 的值,而ID 已经在user_id 索引树上,因此可以直接提供查询结果,不需要回表。
组合索引
单列索引
一个索引只包含单个列
组合索引
一个索包含多个列
最左匹配原则
最左优先,以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(>、<、between、like)就会停止匹配。
where子句几个搜索条件顺序调换不影响查询结果,因为Mysql中有查询优化器,会自动优化查询顺序
B-Tree索引
- 因为存储引擎不用进行全表扫描来获取数据,直接从索引的根节点开始搜索,从而能加快访问数据的速度
- B-Tree对索引是顺序组织存储的,很适合查找范围数据
- 适用于全键值、键值范围或者键前缀查找(根据最左前缀查找)
- 限制:对于联合索引来说,如果不是从最左列开始查找,则无法使用索引;不能跳过索引中的列
B+Tree索引
- 是B-Tree索引的变种,现在主流的存储引擎都不用单纯的B-Tree,而是其变种B+Tree或者T-Tree等等
- 和B-Tree最主要的区别就是B+Tree的内节点不存储data,只存储key,叶子节点不存储指针
Hash索引
- 基于Hash表实现,只有Memory存储引擎显式支持哈希索引
- 适合等值查询,如
=
、in()
、<=>
,不支持范围查询 - 因为不是按照索引值顺序存储的,就不能像B+Tree索引一样利用索引完成排序
- Hash索引在查询等值时非常快
- 因为Hash索引始终索引的所有列的全部内容,所以不支持部分索引列的匹配查找
- 如果有大量重复键值得情况下,哈希索引的效率会很低,因为存在哈希碰撞问题
- 程序员可以在B+Tree索引的基础上创建自适应Hash索引
全文索引
- MyISAM和InnoDB都支持全文索引
- 有三种模式:自然语言模式,布尔模式和查询扩展模式
怎么查看MySQL语句有没有用到索引
通过explain
id:在⼀个⼤的查询语句中每个SELECT关键字都对应⼀个唯⼀的id ,如explain select * from s1 where id = (select id from s1 where name = 'egon1');
第一个select的id是1,第二个select的id是2。有时候会出现两个select,但是id却都是1,这是因为优化器把子查询变成了连接查询
select_type:select关键字对应的那个查询的类型,如SIMPLE
,PRIMARY
,SUBQUERY
,DEPENDENT
,SNION
table:每个查询对应的表名
type:执行查询的访问方法,如const
(主键索引或者唯一二级索引进行等值匹配的情况下),ref
(普通的⼆级索引列与常量进⾏等值匹配),index
(扫描全表索引的覆盖索引)
possible_key:查询中可能用到的索引(可以把用不到的删掉,降低优化器的优化时间)
key:查询中用到的索引
filtered:查询器预测满足下一次查询条件的百分比
extra:表示额外信息,如Using where
,Start temporary
,End temporary
,Using temporary
等
索引失效场景
假设索引为(a,b,c)
ASC
和DESC
索引混合使用的排序:select * from tab order by a, b desc limit 10;
- 违背最左前缀原则:
select * from tab where b = '1';
WHERE
⼦句中出现非排序使⽤到的索引列:select * from tab d = '1' order by a limit 10;
- 排序列包含非同⼀个索引的列:
select * from tab order by a, d limit 10;
WHERE
子句中出现计算:select * from tab where a * 4 = 2;
WHERE
子句中出现null
值:select * from tab where a = null;
WHERE
子句中使用!=
或<>
操作符:select * from tab where a != 1;