索引的模型
哈希表
哈希表是以键值对数据结构存储的,哈希的思路是将值放在数组中,通过对 key 进行运算得到数组的位置,将值放于数组对于的位置。
不可避免的是多个 key 计算的哈希值是相同的,解决哈希冲突的方法主要有两种:开放寻址法,链表法。
需要注意的是:哈希这种结构的值只能用于等值查询,范围查询就需要扫描全表
有序数组
用有序数组作为索引数据结构只适用于静态的存储引擎,并且在等值条件查询和范围查询中的性能非常优越。
树
因为使用树结构再查询时,查询每个节点都需要通过查询磁盘,为了减少查询时的 IO 消耗,一般采用 N 叉树。
InnoDB 索引模型
- 使用的是 B+树结构,每一个索引对应着一颗树。
- 主键索引和非主键索引。
1.主键索引的叶子节点存储是是数据库每行的数据,属于聚簇索引。
2.非主键索引叶子节点存储的是主键的值,使用非主键索引时查询数据需要回表。
索引的维护
在InnoDB引擎中,在插入数据时, 若不是追加主键,就会涉及到数据的移动,在B+树的算法,可能会造成数据页的分裂,
所以在使用非主键索引和非递增主键索引,数据不连续就会造成这个问题,影响性能。
另外,除了会影响性能问题还会影响空间的利用率,如果数据分裂,空间利用率就会降低。
当删除数据时,也会有数据页合并的情况。
什么时候使用主键索引?
- 只有一个索引
- 该字段的值必须是唯一。
- 字段的值避免过长,因为非主键索引叶子节点存储的是主键的值,会造成普通索引的占用空间。
覆盖索引
什么是覆盖索引,比如我们通过普通索引查询主键的值,这时候通过索引就能获取到主键的值,不需要回表就能得到结果,这种情况就称为”覆盖索引”。。
覆盖索引可以减少磁盘的随机 IO 次数,提升查询性能,所以使用覆盖索引也是优化性能常采用的方式。
联合索引
就是表中的多个列构成的一个索引。
索引项按照构成索引的列的字段的顺序排序,
最左匹配原则
只要查询中的条件字段在联合索引中从左边匹配到,就可以使用联合索引,最左匹配原则可以是左边的 N 个字段,也可以是 M 个字符。根据地最左匹配原则我们可通过调整字段的顺序,可以减少一些不必要的索引。
索引下推
在 MySQL5.6之后,增加了索引下推的优化,在遍历索引的过程中,对索引中的字段先做判断,直接过滤不符合条件的记录,减少回表次数。
唯一索引和普通索该怎么选?
change buffer。
- 作用:减少随机访问磁盘的 IO 次数
- 场景 :写多读少的场景
- redo log的关系
1.在内存中,直接更新内存;
2.没有在内存中,就在内存的 change buffer 区域,记录下“我要往 Page 2 插入一行”这个信息
3.将上述两个动作记入 redo log 中
当需要更新一个数据页时,如果数据页在内存中就直接更新,而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InooDB 会将这些更新操作缓存在 change buffer 中,这样就不需要从磁盘中读入这个数据页了。在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。
是可以持久化的数据。也就是说,change buffer 在内存中有拷贝,也会被写入到磁盘上。
将 change buffer 中的操作应用到原数据页,得到最新结果的过程称为 merge。除了访问这个数据页会触发 merge 外,系统有后台线程会定期 merge。在数据库正常关闭(shutdown)的过程
中,也会执行 merge 操作。
为什么查询有时候会选错索引?
- 基数(cardinality);
InnoDB 默认会选择 N 个数据页,统计这些页面上的不同值,得到一个平均值,然后乘以这个索引的页面数,就得到了这个索引的基数,当变更的数据行数超过 1/M 的时候,会自动触发重新做一次索引统计
innodb_stats_persistent 设置为 on的时候 统计信息持久化到磁盘 N= 20 M=10 ;off N= 8 M = 16; - 预估扫描的行数;
为什么我的MySQL会“抖”一下
当内存数据页跟磁盘数据页内容不一致的时候,我们称这个内存页为“脏页”。内存数据写入到磁盘后,内存和磁盘上的数据页的内容就一致了,称为“干净页”。
- redo log 记录满了
这种情况是 InnoDB 要尽量避免的。因为出现这种情况的时候,整个系统就不能再接受更新了,所有的更新都必须停下来。 - 内存不足,需要刷新“脏页”;
InnoDB 用缓冲池(buffer pool)管理内存,缓冲池中的内存页有三种状态:
第一种是,还没有使用的;
第二种是,使用了并且是干净页;
第三种是,使用了并且是脏页。
InnoDB 的策略是尽量使用内存,因此对于一个长时间运行的库来说,未被使用的页面很少。
而当要读入的数据页没有在内存的时候,就必须到缓冲池中申请一个数据页。这时候只能把最久不使用的数据页从内存中淘汰掉:如果要淘汰的是一个干净页,就直接释放出来复用;但如果是脏页呢,就必须将脏页先刷到磁盘,变成干净页后才能复用。所以,刷脏页虽然是常态,但是出现以下这两种情况,都是会明显影响性能的:
1. 一个查询要淘汰的脏页个数太多,会导致查询的响应时间明显变长;
2. 日志写满,更新全部堵住,写性能跌为 0,这种情况对敏感业务来说,是不能接受的。
- MySQL 空闲的时候;
- MySQL 正常关闭的时候;