MySQL8.0: 重新设计的日志子系统

Update

2018/6/19
官方发表了一篇博客介绍内核实现:
MySQL 8.0: New Lock free, scalable WAL design

背景

当前几乎所有的关系数据库都采用日志先行的方式,也就是所谓WRITE-AFTER-LOG(WAL),这是因为日志通常是顺序写的,并且写入量相比修改的数据通常要小很多。通过redo log来确保提交的事务必然具有持久性。(目前也有另外一种理论叫做Write Ahead Log, 由CMU的教授提出,主要适用于Nvme,这里在CMU的peloton项目里有个介绍

然而日志由于要保证顺序性,需要锁来保护所有日志拷贝到buffer都是有序的,引入了一个严重的锁竞争点,特别是在多核场景下,这里的竞争会非常明显,无法发挥出多核心的优势。

为了解决这个问题,MySQL8.0对日志系统进行了重新设计,将整个模块变成了lock-free的模式(小道消息,目前官方也在对事务模块和锁模块改造成lock-free模式,相信到时候InnoDB的扩展性必然会提升一大截, 未来可期!)

具体的,我们可以对应到几个模块:

- 拷贝到buffer: 每个mini transaction将自己的本地日志拷贝到全局Buffer中
- 写磁盘:包括写磁盘和调用fsync进行持久化
- 事务提交:当事务undo被标记为prepare(如果binlog打开) 或者commit时,需要确保日志被刷到磁盘,以确保事务的持久性
- Checkpoint: 定期对日志做checkpoint,减少崩溃恢复时日志的应用量

以下是对上述几个模块的简要介绍

实现

写log buffer

在5.7版本中,Innodb的log buffer实际上是分成了两个区域,轮换着来写,从而实现在写一个buffer 时,另外一个buffer依然可以继续往里面拷贝日志。但到了8.0版本,所有日志相关的mutex都已经移除了,划分缓冲区域也就没有必要了,而是将log buffer当做一个环来使用。

首先,持有一个s lock, 并通过原子操作获取当前mtr的start_lsn,和sn号(lsn减去log block头和尾的大小,表示有效日志量),这样相当于在顺序增加的lsn序列中保留了自己的一段范围(获得mtr_t::start_lsn 和mtr_t::end_lsn), 通过start_lsn取模log buffer size,得到其在log buffer中的位置,然后逐个block进行拷贝(log_buffer_write), 每写一个mtr log block,就将其start_lsn和end_lsn加入到log.recent_written中,维持了一个link结构, 一个mtr 可能会更新多次link_buf

(InnoDB里增加了一个叫link_buf的类,其具体的作用就是将不连续的变量维护成一个链表,举个简单的例子:

原文链接

猜你喜欢

转载自blog.csdn.net/weixin_40581617/article/details/81906834
今日推荐