MySQL B+ 树索引

MySQL B+ 树索引

InnoDB 中索引为 B+ 树结构
每建立一条索引就创建了一棵 B+ 树

结构

每一个索引页内部都是按顺序排列,并且有页目录(索引页结构 <- 点击查看)
多个索引页之间也是按顺序排列,页之间的目录就储存在目录项纪录中 (纪录的是页中索引最小值)
当存在多个目录项纪录后,又需要高一级的目录项纪录来储存这一层的目录
最后就会形成一棵 B+ 树,查找时以多级目录的形式从上向下依次查找

目录项纪录

目录项纪录相当于目录,只存储了索引和页号,用来快速寻找相应的索引页
结构和用户记录相同,唯一不同的就是存储的信息
记录头信息里的 record_type 属性为 1

用户记录

用户记录存储的信息是用户自己定义的
记录头信息里的 record_type 属性为 0

B+ 树结构:

聚簇索引

聚簇索引是指将数据与索引放到了一起
聚簇索引默认是主键,但是可以通过删除主键之后再恢复更改聚簇索引
一个表只有一个聚簇索引
其他的索引都是建立在聚簇索引之上的二级索引

储存内容

聚簇索引中叶子结点储存的是完整的用户记录
目录项纪录中储存的是页中最小主键主键和页号

二级索引 (secondary index)

聚簇索引只能在通过主键查找时才能发挥作用,如果想通过其他列进行查找就需要以相应列建立索引
除了聚簇索引外其他所有的索引都是二级索引 (辅助索引)

储存内容

二级索引中叶子结点储存的只有建立索引的列和主键
目录项纪录中储存的是建立索引的列、主键和页号

回表

因为二级索引中并不储存完整的用户数据,当通过建立索引的列进行查找时,会找到对应的主键,再通过主键在聚簇索引中查找到完整的数据,这个过程就是回表
没有优化时每获取一条二级索引纪录就要进行一次回表
需要回表的记录越多,使用二级索引的性能就越低

使用回表

如果通过二级索引查找到符合条件的数据过多,查询优化器就偏向于使用全表扫描
如果希望它更偏向于通过二级索引查找,可以通过 LIMIT 限制通过二级索引查找到的数据数量,减少回表操作

避免回表 - 覆盖索引

在查询时只查询二级索引的索引列,这样在二级索引中就能获取需要的信息,而不需要再回表去查找完整数据,这个查询就是索引覆盖查询
因此查询列表时最好不要使用 * ,而是标明查询列

顺序 IO

访问二级索引时,由于数据集中分布在一个或几个数据页中,读取速度较快

随机 IO

访问聚簇索引时,通过二级索引查询到的纪录主键不一定连续,所以数据会比较分散,读取速度比较慢

联合索引

如果以多个列建立索引,这个索引就是联合索引

储存内容

联合索引也是二级索引
联合索引中叶子结点储存的是建立索引的所有列和主键
目录项纪录中储存的是建立索引的所有列、主键和页号

与分别建立索引的区别

分别建立索引就意味着要建立多棵 B+ 树,建立联合索引只需要一棵 B+ 树
分别建立索引可以分别进行查找,但是联合索引查找时列的顺序需要按照建立索引的先后进行查找,否则就用不上索引

使用索引

索引并不是建的越多越好:
因为每建一个索引就相当于建立了一棵 B+ 树,占用空间
每次增删改数据时又会修改索引,降低性能

全值匹配

当搜索条件中的列和索引列一致时,就是全值匹配,全值匹配可以使用到索引

查询优化器

WHERE 语句中列的顺序并不会影响查询的顺序,因为查询优化器会对语句进行优化

匹配左边的列

搜索时可以不包括全部联合索引中的列,只包含左边的也可以
由于建立索引时就是从左边开始建的,排序时左边的优先排序,所以搜索时必须要优先搜索左边的列才能用上索引

匹配列前缀

字符串排序时是从最左边的字符开始进行排序
所以原理和匹配最左边的列相同,只有匹配字符串前缀时才能用到索引

# 可以用到索引
SELECT * FROM person WHERE name LIKE 'A%';
# 无法使用索引
SELECT * FROM person WHERE name LIKE '%a%';

如果需要匹配后缀,可以将字符串倒置

匹配范围值

由于 B+ 树中数据是按顺序储存的,所以匹配范围值比较方便
只有最左边的一列范围查找时才能用到索引

精确匹配某一列并范围匹配另外一列

如果左边的列是精确查找,右边的一列进行范围查找时就可以用到索引

排序

如果排序的列顺序与索引相同,则可以直接获取数据
但是如果顺序不同,不同的部分就无法使用索引,左边相同的部分还是可以使用索引,

不能使用索引的情况

当正序 (ASC) 和倒序 (DESC) 混用时就无法使用索引

WHERE 语句中出现了非索引最左一列,而它左边的列却没有出现

排序的多个列不是来自同一个索引

排序列被修饰

分组

如果分组顺序与索引顺序相同,则可以使用到索引,否则只能在文件中进行

建立索引

用适当的方法建立索引,可以提高性能,节省储存空间

只为用于搜索、排序或分组的列创建索引

只为 WHEREORDER BYGROUP BY 子句中的列建立索引
SELECT 语句中的列没必要建立索引

列的基数

列的基数越大,列的数据越分散;基数越小,数据越集中
数据越分散,在二级索引中查询到的数据就会越少,回表操作就会比较少,性能较好
所以尽量为基数大的列建立索引

索引列的类型尽量小

数据类型越小,查询时的比较越快
数据类型约小,索引占用的储存空间就越少,每页中数据越多,磁盘 IO 对性能的损耗就少
尤其是主键,因为聚簇索引和二级索引中都会储存主键

索引字符串的前缀

建立索引时可以只对字符串的前几个字符进行索引
在字符串类型数据存储数据比较多时,建议通过这个方式建立索引

CREATE INDEX index_name ON table_name(column_name(string_length));

既节约空间,又减少字符串的比较时间

缺点

在排序时,由于无法确认完整的字符串,就无法进行排序,所以只能使用文件排序

索引列在表达式中单独出现

例如索引列为 my_col,进行筛选时:

SELECT my_col WHERE my_col * 2 < 4
SELECT my_col WHERE my_col < 4 / 2

虽然语义相同,但是上面那一条无法使用到索引,因为它不是单独出现的
一表达式或函数调用的方式出现都无法使用索引

主键

当插入纪录的主键大小位于现有纪录主键大小之间时,就需要进行页面分裂和纪录移位,来保证纪录的顺序,这就会造成性能损耗
建议将主键设置为 AUTO_INCREMENT, 这样增加新纪录时就只会添加到最后,而不是插入到原有纪录之间

冗余重复索引

如果原有的索引已经可以满足搜索需求时,就不需要再建立新的索引,多余的索引不仅占用空间,还需要维护

posted on 2019-11-22 16:58  doubtful  阅读(977)  评论(0编辑  收藏  举报

导航