Hi~朋友,关注置顶防止错过消息
如何保证数据不丢失?
保证redo log和binlog可以持久化到磁盘,就可以确保MySQL在异常重启后进行数据恢复。
binlog的写入机制
binlog的写入机制逻辑:
事务执行过程中,先把日志写到binlog cache(内存) 事务提交的时候(MySQL客户端执行commit指令),再把binlog cache中写到binlog文件中,并清空binlog cache
操作系统会给每个线程分配binlog cache,binlog cache的大小由binlog_cache_size参数控制,该参数控制的是单个线程内binlog cache的大小,如果超过了该参数的大小,就需要保存到磁盘。
show global variables like 'binlog_cache_size';
binlog cache如何写入binlog文件?

每个线程都有自己的一个binlog cache,但是共同使用同一份binlog 上图中的write指的是把binlog cache写入到文件系统的page cache,不会真正将数据持久化到磁盘,因此速度较快 fsync才是将数据持久化到磁盘(此时会占用磁盘的IOPS)
binlog cache何时write和fsync?
write和fsync是由参数sync_binlog进行控制:
show global variables like 'sync_binlog';
该参数为0,表示每次提交事务只write,不fsync 该参数为1,表示每次提交事务都会fsync 该参数大于1时,假设为N,表示每次事务都会write,但是会累计N个事务进行一次fsync
redo log写入机制
事务在执行过程中,生成的redo log首先会写到redo log buffer redo log会在一些特定条件下写入日志文件
binlog cache是每一个线程一个,但是redo log buffer是所有线程共用一个。
redo log会在哪些地方存在?

redo log会在以下三个地方存在:
绿色部分,MySQL的进程之中,也就是redo log buffer 黄色部分,write到磁盘(存储在Page Cache中),此时没有实际调用fsync写入磁盘 红色部分,持久化到磁盘,调用了fsync
redo log的写入策略如何控制?
redo log写入策略由innodb_flush_log_at_trx_commit参数控制:
show global variables like 'innodb_flush_log_at_trx_commit';
该参数如果为0:表示每次事务提交时只把redo log留在redo log buffer中 该参数如果为1:表示每次事务提交时都将redo log直接持久化到磁盘 该参数如果为2:表示每次事务提交时将redo log写入page cache
InnoDB有一个后台线程,每隔1s,会把redo log buffer中的日志调用write写到page cache,然后调用fsync持久化到磁盘。
事务执行过程中redo log也是直接写入到redo log buffer中能够,这写redo log会因为后台线程会被一起持久化到磁盘(没有提交的事务也会被持久化到磁盘)。
除了后台线程每秒1次的轮询之外,还会有以下两种情况下会将redo log写入磁盘:
redo log buffer占用的空间即将达到innodb_log_buffer_size一半的时候,后台线程会主动写盘。(这里的写盘动作只是写到Page Cache,不会真正调用fsync) 并行的事务在提交时,顺带会将该事务的redo log buffer持久化到磁盘。
MySQL 双1配置是?
MySQL 双1配置指的就是sync_binlog和innodb_flush_log_at_trx_commit都设置成1,也就是说一个事务完整提交前,需要等待两次刷盘,一次是redo log(prepare阶段),一次是binlog。
什么是日志逻辑序列号(LSN)?
LSN是用来对应redo log的一个个写入点,是单调递增的,每次写入长度为length的redo log,LSN的值就会加上length。
LSN也会写到InnoDB的数据页中,来确保数据页不会被多次执行重复的redo log。
什么是组提交(Group Commit)?

trx1是第一个到达的,会被选为leader 等trx1要开始写盘的时候,该组里有三个事务,此时LSN会变成160 trx写盘的时候,带的LSN=160,因此当trx1返回时,所有LSN小于等160的redo log都已经被持久化到磁盘 trx2和trx3此时就可以直接返回
一次组提交中,组员越多,越能节约磁盘IOPS。在多事务并发更新场景下,第一个事务写完redo log buffer以后,fsync越晚调用,组员就可能越多,节约IOPS效果越好。
binlog的组提交
上面的组提交是redo log组提交,MySQL为了充分提高性能,binlog也会进行组提交。

上图最后三个步骤是redo log和binlog的两阶段提交(该步骤的触发是事务提交的一个阶段,也就是说我们的客户端执行了MySQL的commit命令),在写binlog的时候实际需要两步操作:
先把binlog从binlog cache中写到磁盘上的binlog文件 调用fsync持久化

上图是两阶段提交的细化,可以看出在执行第4步binlog fsync时,如果有多个事务的binlog已经写完,那么是可以一起持久化的,可以降低IOPS的消耗。
由于步骤3的执行较快,binlog write和fsync间隔时间段,所以binlog的组提交效果较差。
binlog组提交效果提升
如果需要提升binlog组提交的效果,可以通过设置binlog_group_commit_sync_delay和binlog_group_commit_sync_no_delay_count参数来实现:
show global variables like 'binlog_group_commit_sync_delay';
show global variables like 'binlog_group_commit_sync_no_delay_count';
binlog_group_commit_sync_delay:表示延迟多少微秒以后调用fsync binlog_group_commit_sync_no_delay_count:表示累计多少次以后调用fsync
上述两个条件是或关系,只要满足一个就会触发fsync。
redo log的commit阶段
根据两阶段提交的细化流程图,我们可以发现redo log的commit只写了page cache,并没有进行fsync,这是因为借助每秒1次的后台轮询刷盘,再加上崩溃恢复逻辑,InnoDB认为在redo log commit的时候就不需要fsync,只需要写到page cache即可。
本期MySQL数据可靠性就到这,扫码关注,更多内容我们下期再见!

往期推荐




