MySQL --- 读书笔记 --- 索引(2)
为什么需要索引
数据库的数据不是连续存储在磁盘上的,假设查找数据的方式是逐条查询,那么最坏情况下,查询一个数据就需要一次磁盘I/O,那么消耗在I/O上的时间就会非常大。
那么如果我们为数据的存储建立一个索引表,就像是书本的目录一样,然后再使用二叉树这样的数据结构来查找数据,那么我们知道,二叉树的检索时间复杂度是对数级别的,也就是说,磁盘的I/O次数从线性级别减低到对数级别了,这样大大提高了检索速度
索引概述
- 索引的本质,是一种有序的数据结构,可以满足特定的查找算法,然后以某种方式指向真实数据
- 索引是在存储引擎中实现的,所以不同的存储引擎可能有不一样的索引结构
索引的优点
- 降低磁盘I/O次数,提高检索效率
- 天然有序,降低有序查询的时间
- 对于有依赖关系的两张表,可以加速表之间的连接
- 建立唯一索引,保证数据的唯一性
索引的缺点
- 创建索引和维护索引需要耗费时间,随着数据量的增加,耗费时间也会增加
- 索引也需要占用磁盘空间
- 索引的建立可以提高查询速度,但是也会降低表更新的速度,对表的增、删、改,索引也会动态地维护,这就降低了数据的维护速度
聚簇索引
- 聚簇索引指的是一种数据存储方式,所有的记录都存储在叶子结点,索引即数据,数据即索引
- 数据页内的记录是按照主键大小顺序排成一个单向链表
- 数据页之间也是按照页中记录主键大小顺序排成一个双向链表
优点
- 数据访问快,聚簇索引将数据和索引保存在同一个B+树中,因此这样获取数据更快
- 聚簇索引对于主键的排序查找和范围查找更快
- 相邻数据是紧密联系的,所以在范围获取数据时,会减少I/O次数
缺点
- 插入速度依赖插入顺序,按照主键的顺序插入数据是最快的,否则会出现页分裂,影响性能
- 更新主键的代价很高,一般不允许主键的更新
- 二级索引的访问需要两次索引查找,一次找到主键,一次根据主键找到记录
二级索引
- 以其他列的数据,建立另一个B+树,叶子结点的内容不带有全部数据,只有列的数据以及对应的主键值
联合索引
- 以多个列数据作为排序规则,建立一个B+树,它首先会按照第一个列的顺序排列,当第一个列出现相同数据时,按照第二个列的顺序排列,以此类推;最后叶子结点也是只有列的值以及对应的主键
B+树索引的注意事项
- 根页面位置万年不动
- 每当为某个表创建一个索引时,都会为这个索引创建一个根节点页面,最开始没有数据的时候,根节点中没有记录也没有目录项
- 当开始向表中插入记录时,会先把记录存储在根节点中
- 当根节点中的空间用完之后,此时会将根节点中的数据复制到一个新的页中,然后对这个页进行页分裂,得到一个新页,此时新记录会放在两个新的页中,然后根节点就会升级为只存储目录项的页
- 然后在根节点中的目录项记录满了之后,重复上面的操作,但是根节点依旧不动摇
- 内节点中目录项记录的唯一性
- 当二级索引中的列都一样的时候,在插入一条新记录时,无法区分应该在哪个页中记录
- 为了让新记录能够找到所在页,我们需要保证在B+树中的同一层节点的目录项记录除了页号以外是唯一的,所以在目录项中,除了有索引列和页号,还有主键
- 一个页最少存储2条记录
索引的代价
- 空间的代价:每建立一个索引,都要为其建立一个B+树,每一个树结点都是一个数据页,每个数据页默认是16KB
- 时间的代价:每次增删改,都需要对每个索引进行修改,那我们知道,每个索引树,无论是目录项层次或者叶子结点层次还是页结点内部的记录,都是按照索引列的大小排序的,那么经过增删改之后,节点的顺序会被破坏,那就需要额外的时间进行一些记录位移,页分裂,页面回收等操作来维护索引树;所以索引越多,增删改的性能就越差
不选择Hash索引的理由
- Hash索引只能满足确定性的查找,例如(=),(!=),如果进行范围查找,会退化成全表检索,但是树的有序性,会保持查找的高效
- Hash索引是无序的,在进行一些带有排序的查找时,需要二次排序
- 联合索引下,Hash索引需要两个列联合才可以查找,没办法对一个或者几个列查找
- 对于等值查找,Hash索引效率很高,但是当索引列有很多重复值时,Hash索引会遇到Hash冲突,那么查找会变得耗时
- 相比之下,树结构的索引的适用性更加广
- InnoDB本身不支持Hash索引,但是提供自适应Hash索引,对于某个经常被访问的数据,当满足一定条件时,会将这个数据页的地址放到Hash表中
B+树与B树的差异
- 有K个孩子就有K个关键字,B树有K个孩子,就有K-1个关键字
- 非叶子节点的关键字也会同时存在于子节点中,并且是子节点中所有关键字的最大或最小
- 非叶子节点仅仅用于索引,不保存数据,所有记录信息放在叶子节点,在B树中,非叶子节点也会保存记录信息
- 所有关键字都在叶子节点中出现,叶子节点构成一个有序的链表
B+树索引中间节点不存储数据有什么好处
- 查询效率更加稳定,因为需要获得数据,每次都必须访问到叶子节点才可以
- 查询效率更高,中间节点只存放索引信息,导致在相同页节点大小可以存放的目录信息更多,那就导致分叉越多,然后B+树的树高更小,磁盘I/O也就更少,速度也就更快
- 范围查询效率也更高,因为所有数据都在叶子节点,而叶子节点之间会有有序链表连接,那就可以用指针连接查找,速度更快
B+树的存储能力
InnoDB中的页大小是16KB,一般表的主键大小是4/8byte,一个指针一般是4/8byte,也就是说一个页中大概可以存储(16/(8+8)=1K)个记录。
假设树高是3,那么最终叶子节点是有1k * 1k * 1k = 10亿
条记录(假如叶子节点也是存储1k个数据)
InnoDB在设计时将根节点常驻在内存中,那么每次查询,最终的磁盘I/O次数不会超过3次,因为树高一般不超过4层