目录
前言:
1.InnoDB索引模型
2 索引分类
是否具有主键约束:主键索引与二级索引
是否具有唯一约束:唯一索引和普通索引
索引字段数量:联合索引和单列索引
2 索引维护
索引重建操作
非主键索引
主键索引
索引重建流程
接上一篇MySQL进阶:(二) 日志模块,索引是在存储引擎层实现的,索引在MySQL不同存储引擎中的实现是不同的,本篇介绍的是InnoDB的索引;
使用InnoDB存储引擎创建的表又叫做索引组织表,因为表中每一条记录(数据行)的物理顺序都是按照主键顺序
使用InnoDB引擎建立的表又称索引组织表,因为表中记录的物理顺序都与主键索引树的主键顺序保持一致,而具备该特点的主键索引又被称作聚集(聚簇)索引;
同一张表中每一个索引都对应着一棵B+树,当使用某个索引时,会先在该索引树上检索,因为索引项是有序的,所以可以直接利用二分查找快速定位叶子结点的位置;当没有使用任何索引时,比如select * from T会在主键索引树上全扫描。注意,按照SQL的执行顺序,即使添加上limit 1也无法避免全扫描;
不同类型的索引的使用机制和使用场景都是不同的,这里先看下索引的分类;
顾名思义,具备主键约束的索引是主键索引,主键索引以外的索引是二级索引;为了方面理解,这里先准备一张InnoDB表,主键是id,二级索引是K
主键索引又称聚集索引,特点是
1.中间节点存放的是主键
2.叶子结点的内容是数据页(Page),一个数据页可以放多个数据行(记录);
二级索引的特点是
1.中间节点的内容是二级索引
2.叶子结点的内容是主键
主键索引的选取规则:
1.推荐使用自增索引,从性能的角度出发,非自增主键在插入和删除的操作中,会导致页分裂和页合并。
2.主键列长度尽可能短,从空间的角度出发,每个二级索引的叶子结点是主键,主键过长会导致二级索引占用空间更大;
记录都是存放在数据页中,数据页可以存放多条记录,当插入新的记录时:1.如果插入位置位于数据页中间,这时候需要把数据页中后续的记录全部后移;2.如果数据页已经存满了记录,这时候需要申请空白数据页,并将最后一条记录移动到空白数据页;
页分裂需要后移记录,甚至是申请空白数据页并后移,不但影响性能,还会降低页使用率;
当相邻两个数据页由于删除数据,导致页利用率很低时,会将数据页合并,这时候需要挪动记录到同一个数据页;
对比非自增主键,自增主键的插入都是队尾插入,减少了页分裂;
当然,自增不等于要使用autoincrement约束,可以在业务上实现自增。自增不是绝对的,比如说不涉及更新的历史数据表。
主键列长度短也不是绝对的,比如说没有二级索引的表,甚至是只有单列数据的表;
具备唯一约束的索引是唯一索引,没有明确说明的索引一般都是非唯一索引。
不推荐使用唯一索引,这里分别从查询和更新操作分析下唯一索引和非唯一索引:
查询:
当然,这里有个临界情况:当查询结果刚好位于内存中的最后一个数据页的尾部,需要先将后续数据页从磁盘读取到内存再判断是否存在满足查询条件的记录;
这种概率是很低的,存储引擎一次性会读取多个数据页到内存,每个数据页包含多条记录(整型数据,可以达到2000条以上);并且叶子节点之间是以链表形式存在的,查找下一个叶子结点无需再通过中间节点二分查找;
更新:
唯一索引:需要先将需要更新的记录从磁盘中加载到内存,更新内存记录并写redolog;
普通索引:将更新操作写入change buffer,通知执行器更新完成;在下次读相关记录的时候,先把原记录读取到内存,再将change buffer上的操作在内存记录上回放,并写redolog;
这里可以看到,普通索引在更新时,节省了更新时从磁盘读取记录的时间,而唯一索引在更新时,若记录不在内存,需要从磁盘读取记录到内存;
单列索引字段只有一列,联合索引字段包含多列。上面提到过索引项都是有序的,字符串索引也可以看做是特殊的联合索引,因为二者的顺序是由索引内字段/字符的定义顺序决定的,比如联合索引index(a,b)和字符串索引index(ab)都是先比较a的大小进行排序,当a相等时,再按照b的大小进行排序;最左原则也是按照该规则进行设计的(下一篇会提到);
InnoDB引擎中的数据是按页存储的,每页包含若干个数据行(记录),当记录删除时,该位置不会被回收,而是被标记为可复用,即可以插入新的记录,而这种数据页的不完整也叫做页空洞。但是当数据页已存在其他记录时(可理解为该数据页正在被使用),对于插入的记录有范围要求;当数据页中的所有记录都为空时,该数据页也不会被回收,而是被标记为可复用,当插入新记录申请数据页的时候使用;重建索引就是为了解决页空洞问题,提升数据页利用率;
主键与非主键索引的重建是不同的
alter table T drop index k;
alter table T add index k;
alter table T engine=InnoDB
alter table T drop PRIMARY key; (1)
alter table T add PRIMARY key(id); (2)
原因:(1)(2)两步会触发两次表重建
为了避免转存数据的过程中旧表的更新操作造成数据不一致,所以在转存时旧表无法进行更新操作;
记录日志+日志重放的功能实现拷贝旧表数据时不影响旧表更新操作;