《丁奇-MySQL45讲-23/24》之归纳总结
23 | MySQL是怎么保证数据不丢失的?
- 事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。
每一个线程都会分配一个binlog cache,由参数binlog_cache_size控制单个线程内binlog cache的大小,如果超过了这个参数规定的大小,那么就会暂存到磁盘的临时空间中。在事务提价后,会清空binlog cache。每个线程都有自己的binlog cache,但是它们共用一份binlog文件。

binlog的刷盘机制:

-
图中的write,指的是把日志写入到文件系统的page cache,并没有把数据持久化到磁盘上。
-
图中的fsync,才是将数据持久化到磁盘上。
write和fsync的时机,是由参数sync_binlog控制的:
-
sync_binlog=0,表示每次提交事务只write,没有fsync。
-
sync_binlog=1,表示每次提交事务都fsync。
-
sync_binlog=N(N>1),表示每次提交事务都write,但累积N个事务后才fsync。对应的风险是,如果主机发生异常重启,会丢失最近N个事务的binlog日志。
redo log的刷盘机制:

图中的三种颜色同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,表示每次事务提交时都只是把redo log写到page cache。
InnoDB有一个后台线程,每隔1秒,就会把redo log buffer中的日志,调用write写到文件系统的page cache,然后调用fsync持久化到磁盘。
还有一个是redo log buffer占用的空间即将达到innodb_log_buffer_size一半的时候,后台线程会主动写盘。注意,由于这个事务并没有提交,所以这个写盘动作只是write,而没有调用 fsync,也就是只留在了文件系统的page cache。
另一种是,并行的事务提交的时候,顺带将这个事务的redo log buffer持久化到磁盘。假设一个事务 A 执行到一半,已经写了一些redo log到buffer中,这时候有另外一个线程的事务B提交,如果innodb_flush_log_at_trx_commit设置的是 1,
那么按照这个参数的逻辑,事务B要把redo log buffer里的日志全部持久化到磁盘。这时候,就会带上事务A在redo log buffer里的日志一起持久化到磁盘。
组提交(group commit)模式。其中,LSN是日志逻辑序列号,是单调递增的,用来对应redo log的一个个写入点。每次写入长度为length的redo log,LSN的值就会加上length。
-
trx1是第一个到达的,会被选为这组的leader。
-
等trx1要开始写盘的时候,这个组里面已经有了三个事务,这时候LSN也变成了160。
-
trx1去写盘的时候,带的就是LSN=160,因此等trx1返回时,所有LSN小于等于160的redo log,都已经被持久化到磁盘。
-
这时候trx2和trx3就可以直接返回了。
所以,一次组提交里面,组员越多,节约磁盘IO的效果越好。在并发更新场景下,第一个事务写完redo log buffer以后,接下来这个fsync越晚调用,组员可能越多,节约IO的效果就越好。
-
为什么binlog是不能被"打断的",而redo log却可以呢?binlog属于逻辑日志,一个线程中同时执行的事务只有一个,如果一个事务被拆开成多个事务来执行的话,是会破坏原子性的。redo log属于物理日志,记录了数据页的变化,压根就不用在乎
数据页上的修改是否属于同一个事务的,而且也没有意义。 -
binlog组提交:
-
binlog_group_commit_sync_delay参数,表示延迟多少微秒后才调用fsync。
-
binlog_group_commit_sync_no_delay_count参数,表示累积多少个提交事务以后才调用fsync。
这两个条件是或的关系,也就是说只要有一个满足条件就会调用fsync。当binlog_group_commit_sync_delay设置为0的时候,binlog_group_commit_sync_no_delay_count就相当于没用了。
- sync_binlog=N 表示每个事务write后就响应客户端了,当达到N个事务后才开始刷盘;binlog_group_commit_sync_no_delay_count=M 表示事务达到M个后才会提交,会增加客户端的响应时间。如果这两个都配置的话,那么会先判断
binlog_group_commit_sync_no_delay_count,只有满足M后才会继续判断sync_binlog的逻辑,只有满足N后才会进行刷盘,其中如果sync_binlog=0,则最终是不会刷盘的。
浙公网安备 33010602011771号