Mysql的innoDB存储引擎是怎么实现数据持久化的?一
面试官:数据库的ACID属性了解吗?
xx:就是原子性、一致性、隔离性、持久性。
面试官:就没了?那你知道Mysql的innoDB存储引擎是怎么实现 D属性 持久性的呢?
xx:这个是通过redo log保证。
面试官:(又没了?)那你回去等通知吧。
可能前两年的面试回答这个问题,用redo log回答已经可以了,不过现在的要求越来越高,显然搪塞不过去了。要想了解这个问题,首先要知道Buffer Pool。
InnoDB以页(默认大小为16K)为单位,从磁盘文件中读取数据到内存。记录更新也是以页为单位刷新到磁盘中。每一页中的记录都是按照索引大小顺序排序。可以理解为Buffer Pool是在内存中缓存着某些页里的数据,事实上不仅仅缓存着页数据,还缓存着索引、自适应哈希索引、插入缓存(Insert Buffer)、锁、以及其他内部数据结构。
如果没有Buffer Pool,读写效率为什么会低?
模拟一个select查询场景(假设没有Buffer Pool)
select * from order where order_id = 'xxx123';执行这个sql,InnoDB首先需要找到order_id = 'xxx123'这条记录所在的页,把这页读取到内存,然后从这页的所有记录中找到order_id = 'xxx123'那条记录返回。每次这样的查询搜索都会进行磁盘IO,而磁盘IO的速度相对于内存读写的速度、CPU计算的速度是很慢的。
再模拟一个update更新场景(假设没有Buffer Pool)
update order set user_name = '张三' where order_id = 'xxx123';同样的InnoDB首先需要找到order_id = 'xxx123'这条记录所在的页,把这页读取到内存,然后从这页的所有记录中找到order_id = 'xxx123'那条记录并更新user_name列为张三,再刷回磁盘。其中的查询搜索会进行磁盘IO,刷回磁盘的操作又出现随机写IO,就更慢了。
所以显然,如果没有Buffer Pool,只是从磁盘读写的话,InnoDB的读写效率是极低的。《高性能Mysql第三版》P342也直接写到:InnoDB严重依赖Buffer Pool。
当引入Buffer Pool之后,select查询场景时就会变成先在Buffer Pool中查找order_id = 'xxx123'这条记录所在的页,如果该页存在,则直接查询到记录返回,如果该页不存在,则从磁盘进行读取,放入Buffer Pool,返回记录。同样的,update更新场景时就会变成先在Buffer Pool中查找order_id = 'xxx123'这条记录所在的页,如果该页存在,则直接在该页上更新,如果该页不存在,则从磁盘进行读取,放入Buffer Pool,再在该页上更新。(这里还有一个写入redo log的过程)
上面update场景下,时候磁盘中order_id = 'xxx123'对应的记录user_name还是"李四" ,而Buffer Pool中user_name则为“张三"。磁盘与Buffer Pool中不一致,这里Buffer Pool中的页就称为脏页。 当超过InnoDB缓冲池中最大允许的脏页面的比例,
show VARIABLES like 'innodb_max_dirty_pages_pct';默认是75%,强制进行checkpoin,刷新一部分的脏页到磁盘。InnoDB默认通过一个后台线程来刷新脏页,并且会合并写入,更高效的顺序写出到磁盘;自适应刷新,无需等innodb_max_dirty_pages_pct的75%到了再刷新脏页,自行根据redo log的产生速度会优先刷新部分脏页
show variables like 'innodb_adaptive_flushing';默认是开启的。
这里还有一个问题,当Buffer Pool已经满了
--查看Buffer Pool容量大小 show VARIABLES like 'innodb_buffer_pool_size';而有需要新的磁盘页加载到内存页该怎么做?这里也是使用到了LRU的算法思想,(我前面的文章有专门写LRU算法)将最近最少使用的页面淘汰,释放空间来加载新的页面。
到这里就差不多把innoDB的读写效率高的原理讲完了,持久性还需要redo log来保证,redo log下期再写。
扫码关注峡谷程序员![免费获取更多学习资料]