//MySQL8.0的redo log优化//
昨天的内容中,我们介绍了MySQL中的MTR的概念,它是指最小的原子事务操作。当redo log以MTR为单位提交时,需要借助mutex这个轻量的锁,在该锁的保护下,MySQL做了2件事情:
1、redo log写入到全局redo log buffer中,
2、同时将事务修改的数据页添加到flush_list中。
因此在多个线程同时写redo log buffer的时候,会产生mutex竞争。
01
mutex拆分
在MySQL8.0之前,这个mutex只有一个,称之为log_sys_t:mutex。在MySQL8.0之后,这个mutex拆分成了2个,分别为log_sys_t:flush_order_mutex和log_sys_t:mutex。这里我们分别简称它们为flush_mutex和mutex
其中:
flush_mutex主要用于将修改的数据页加入到flush_list;
mutex则专注于将redo log拷贝到全局的redo log buffer中;
这样,分工似乎更加明确了。拷贝redo log的线程就可以和添加数据页到flush_list的线程并发起来了。
02
双log buffer机制
除此之外,MySQL8.0的日志系统还引入双redo log buffer的功能,其中,一个log buffer用于拷贝连接会话的redo log到全局log buffer,另一个log buffer用于读取、写入log buffer中的内容到iblogfile文件。当需要读第一个log buffer的时候,则可以切换两个log buffer的角色。利用这种方法,就解决了并发场景下访问全局redo log buffer的锁竞争问题。
03
并发写redo log优化
mutex拆分出来flush_mutex之后,虽然解决了单个连接会话的redo log写入全局redo log buffer和修改的page到flush_list的并发,但是当连接会话过多的时候,同时将redo log写入redo log buffer的并发问题还没有解决。
MySQL8.0引入原子变量预占位的技术解决了redo log的并发写入问题,预占位可以理解每个会话提前占领一块redo log buffer的空间,这样因为空间已经划分好了,所以不需要等待其他redo写完,就可以并发进行redo log buffer的写入。听着比较拗口,举个例子:

三个会话同时写redo log buffer,因为是并发写的,而且写的速度不一样,因此带来了2个问题:
1、很可能导致LSN较大的redo log 先写完,也就是redo log 写入顺序不是递增的。
2、同时,由于预占位技术,会使得redo log中间存在一部分空洞。
MySQL为了解决这2个问题,MySQL后台的写日志线程首先维护了一个滑动窗口,找到最小连续的lsn的最大值LSN_1,然后将这个LSN之前的redo log从buffer中写入日志里面,接着推进LSN_1的值。
04
脏页有序加入到flush_list优化
这部分内容比较多,我们暂时忽略,下次再说,先知道MySQL8.0对这部分也进行了优化即可。
05
写日志线程优化
MySQL8.0还将写redo log线程从用户线程中拆分出来,有单独的log writer线程和log flusher后台线程来处理写redo日志和sync redo日志。MySQL8.0之前,redo log是随机地进行group-commit,由一个线程负责write/flush日志,其它线程等待,这种模式下group的大小比较随机。拆分后,处理更灵活,对于flush_log_at_trx_commit!=1的场景,只需要等log writer的通知就认为事务执行完毕,sync 日志的动作后续异步完成即可。