Mysql整理记录Day4
通过前几篇文章的学习,我们知道Mysql主要是依靠 redo log 和 binlog 这两个日志来保证数据不丢失的。
那 redolog 和 binlog 的写入流程是怎样的?今天我们就来聊聊这个话题
binlog的写入机制
其实,binlog的写入逻辑比较简单,事务执行的过程中,先把日志写到binlog cache,事务提交的时候再写到binlog文件。注意,一个事务的binlog是不能被拆开的,因此不管这个事务有多大,都需要保证一次性写入,这就涉及到binlog cache的保存问题。
在了解binlog的写入机制之前,我们需要有这么几个概念
- 系统给binlog cache分配了一段内存,每个线程有自己的 binlog cache,但是共用一份binlog文件, binlog_cache_size 参数用来控制每个线程中binlog cache的大小,如果超过了 这个参数,就需要暂存到磁盘。
- 下图中的write,指的是将binlogcache的日志写入到文件系统的pagecache中,并没有持久化到磁盘,所以速度比较快。(补充:page cache是文件系统中的概念,是文件系统向内核申请的一段内存。后面说到的mysql异常重启,不会影响page cache保存的数据。只有当操作系统断电或者异常重启时,page cache的内容才会丢失。)
- 下图中的fsync,才是将数据持久化到磁盘,一般情况下,我们认为只有fsync才占磁盘的IOPS。
write和fsync的时机是由参数sync_binlog控制的:
- sync_binlog = 0时,表示每次提交事务,直接write,不fsync;
- sync_binlog = 1时,表示每次提交事务,都会执行fsync;
- sync_binlog = N(N>1)时,表示每次提交事务都write,在累积到N个事务的时候,调用fsync。
因此,在IO瓶颈的场景里,将sync_binlog设置为一个比较大的值,可以提升性能,降低 IOPS 消耗。但是,对应的风险就是,如果主机异常重启,会丢失这N个事务的binlog。实际业务中,考虑到丢失日志的可控性,一般设置为100~1000。
redo log的写入机制
事务执行过程中,redo log是先写到redo log buffer的。redo log buffer是Mysql进程向系统申请的一段内存。所有的线程共用这段内存空间。
InnoDB 提供了参数 innodb_flush_log_at_trx_commit 来控制 redo log 的写入策略:
- 设置为0,表示每次事务提交都只是把 redo log 保留在 redo log buffer 中;
- 设置为1,表示每次事务提交都将 redo log 持久化到磁盘;
- 设置为2,表示每次事务提交都只是把 redo log 调用 write 写到 page cache。
InnoDB 后台有一个线程,每隔1s,调用 write 写入到 page cache,再调用 fsync 持久化到磁盘。注意,事务执行过程中的 redo log 也是直接写在 redo log buffer 中的,这些 redo log 也会被后台线程一起持久化到磁盘。也就是说,一个没有提交事务的 redo log 也可能被持久化到磁盘。
以下两种场景也会让一个没有提交事务的redolog写入到磁盘:
第一种:redo log buffer 占用空间即将达到参数 innodb_log_buffer_size 的一半,后台线程主动写盘。(只是写到文件系统的 page cache);
第二种:并行事务提交的时候,顺带将另外一个没提交事务的redo log持久化到磁盘。
通常我们说的Mysql 的双“1”配置,指的是 sync_binlog 和 innodb_flush_log_at_trx_commit 都设置为1。也就是说,一个事务完整提交前,需要两次刷盘,一次是redo log(prepare阶段),一次是bin log。
假设你从Mysql看到的TPS是每秒2万的话,那么是不是每秒会有四万次刷盘?实际上不是,因为redo log的组提交机制,大大节约率磁盘的IOPS。
LSN(日志逻辑序列号),单调递增,对应一次次redo log的写入点,每次写入len长度的redolog,LSN就增加len。
笔记参考于极客时间《MySQL实战45讲》