1、MySQL 的 change buffer 是什么?
当需要更新一个数据页时,如果数据页在内存中就直接更新;而如果这个数据页还没有在内存中的话,在不影响数据一致性的前提下,InnoDB 会将这些更新操作缓存在 change buffer 中。
这样就不需要从磁盘中读入这个数据页了,在下次查询需要访问这个数据页的时候,将数据页读入内存,然后执行 change buffer 中与这个页有关的操作。通过这种方式就能保证这个数据逻辑的正确性。
注意唯一索引的更新就不能使用 change buffer,实际上也只有普通索引可以使用。
适用场景:
对于写多读少的业务来说,页面在写完以后马上被访问到的概率比较小,此时 change buffer 的使用效果最好。这种业务模型常见的就是账单类、日志类的系统。
反过来,假设一个业务的更新模式是写入之后马上会做查询,那么即使满足了条件,将更新先记录在 change buffer,但之后由于马上要访问这个数据页,会立即触发 merge 过程。这样随机访问 IO 的次数不会减少,反而增加了 change buffer 的维护代价。
2、MySQL 是如何判断一行扫描数的?
MySQL 在真正开始执行语句之前,并不能精确地知道满足这个条件的记录有多少条。
而只能根据统计信息来估算记录数。这个统计信息就是索引的“区分度。
3、为什么 redo log 具有 crash-safe 的能力,是 binlog 无法替代的?
第一点:redo log 可确保 innoDB 判断哪些数据已经刷盘,哪些数据还没有
redo log 和 binlog 有一个很大的区别就是,一个是循环写,一个是追加写。也就是说 redo log 只会记录未刷盘的日志,已经刷入磁盘的数据都会从 redo log 这个有限大小的日志文件里删除。binlog 是追加日志,保存的是全量的日志。
当数据库 crash 后,想要恢复未刷盘但已经写入 redo log 和 binlog 的数据到内存时,binlog 是无法恢复的。虽然 binlog 拥有全量的日志,但没有一个标志让 innoDB 判断哪些数据已经刷盘,哪些数据还没有。
但 redo log 不一样,只要刷入磁盘的数据,都会从 redo log 中抹掉,因为是循环写!数据库重启后,直接把 redo log 中的数据都恢复至内存就可以了。
第二点:如果 redo log 写入失败,说明此次操作失败,事务也不可能提交
redo log 每次更新操作完成后,就一定会写入日志,如果写入失败,说明此次操作失败,事务也不可能提交。
redo log 内部结构是基于页的,记录了这个页的字段值变化,只要crash后读取redo log进行重放,就可以恢复数据。
这就是为什么 redo log 具有 crash-safe 的能力,而 binlog 不具备。
4、当数据库 crash 后,如何恢复未刷盘的数据到内存中?
根据 redo log 和 binlog 的两阶段提交,未持久化的数据分为几种情况:
change buffer 写入,redo log 虽然做了 fsync 但未 commit,binlog 未 fsync 到磁盘,这部分数据丢失。
change buffer 写入,redo log fsync 未 commit,binlog 已经 fsync 到磁盘,先从 binlog 恢复 redo log,再从 redo log 恢复 change buffer。
change buffer 写入,redo log 和 binlog 都已经 fsync,直接从 redo log 里恢复。