之前的文章介绍过,mysql 的日志是保证数据恢复的关键。那么日志肯定是要持久化到磁盘的,不然也会出现断电或者重启丢失的问题。那么接下来,我们将详细介绍下数据库的日志是怎么持久化到磁盘的。
binlog写入磁盘的顺序大致是这样的,首先写入binlog cache,然后写入page cahe ,最后持久化到磁盘。如下图
write 和 fsync的机制,都是由参数sync_binlog控制的
sync_binlog = 0 时 每次提交事务只 write 不fsync
sync_binlog = 1 时 每次提交事务都会fsync
sync_binlog = n 时 每次提交事务都会 write ,但是攒到n个事务之后,再一起提交
redo log 日志刚开始时放在redo log buffer 里面,之后才会写道page cache里面,最后才会持久化到磁盘。但是,它们两个的写入机制有一点不一样,就是 redo log buffer 是公用的,但是binlog cache 是每个线程单独的。
与binlog 一样,redo log同样受到innodb_flush_log_at_trx_commit 参数控制
innodb_flush_log_at_trx_commit = 0 的时候,每次提交都是把事务留在redo log buffer
innodb_flush_log_at_trx_commit = 1 的时候,每次提交都持久化到磁盘
innodb_flush_log_at_trx_commit = 2 的时候,每次提交都只写道page cache 里
听完上面的描述,大家有没有发现一个问题。那就是redo log buffer 既然是公用的,那么它会不会存在用的事务提交了,有的事务还没提交,都一起被写入到磁盘了。答案是有的,造成这个现象有以下几种场景。
1.因为innodb 会每隔一秒就会把redo log buffer 里面的内容持久化到磁盘(不知道这个会不会和上面参数的设置冲突)
2.redo log buffer 占到innodb log buffer size的一半的时候,后台线程会将redo log buffer里面的日志,持久化到磁盘(这个有点像刷脏页操作)
3.当有一个事务提交之后,持久化日志到磁盘的时候,可能会顺带着把没有提交事务的日志也给持久化到磁盘。
这个算是日志持久化的一个优化机制。首先说下日志逻辑序列号lsn,因为日志是顺序写入的,所以lsn是单调递增的,假如事务a的写入点为50,事务长度为20,假设只有事务a的情况下。事务a提交的时候,它持久的日志部分就是50到70.
组提交其实就是,假设事务a开始的时候在50,但是期间又有事务b,事务c等等,所以当它提交的时候lsn可能直接是150,那么持久化的时候,就会直接将50到150的全部给持久化了。明白这个,你就理解上面有的事务还没提交都一起被写入到磁盘的第三个场景了。
同样的,组提交也受 binlog_group_commit_sync_delay binlog_group_commit_sync_no_delay_count 控制。binlog_group_commit_sync_delay 参数,表示延迟多少微秒后才调用 fsync;binlog_group_commit_sync_no_delay_count 参数,表示累积多少次以后才调用 fsync。
其实你仔细看过上面的介绍,大致就应该知道了。
1.就是设置binlog_group_commit_sync_delay 和binlog_group_commit_sync_no_delay_count 参数,尽可能的保证每次写磁盘,多写入一些数据。
2.将sync_binlog = n,尽量等事务多一些,再写入磁盘。
3. innodb_flush_log_at_trx_commit = 2,目的和上面两个差不多一样。
本篇文章详细介绍了日志的持久化机制。同时,通过分析总结了一些io性能的优化建议。希望大家日后遇到对应业务场景的时候可以派上用处。