Mysql是如何保证数据不丢失的
binlog
- 事务执行过程中binlog写入到binlog cache,事务提交时写入到binlog文件中。
- 事务的原子性决定这无论事务有多大,binlogcahe都要一次性完整的写入到binlog文件中,写入方式如下:
- 系统为每个binlog_cache开辟了一片内存(每个线程都有一个),参数是blong_cache_size,超过这个阈值的会保存在临时文件中
- 事务提交时候,出于性能的考虑回将bionlog_cache都write到文件系统的page_cache中,在通过fsync刷新到磁盘文件中。如下图所示是binlog的同步机制。
如图:
- 只有fsnyc刷盘这不操作才会占用IOPS。
- write和fsync的时机是用binglog_sync参数来控制的
- =0 每次事务提交时候都只write不sync
- =1 每次事务提交都sync
- =N(N>1) 每次事务提交都write,积攒到N时候才会fsync
在IO出现瓶颈时候我们可以设置一个较大的值来提升性能(个人不是很推荐做,如果出现这种情况建议从业务考虑优化数据库)
redolog的写入机制
redolog从写入到最终写入到磁盘中会经历如下的阶段:写入redolog-buffer中–>在事务提交、或者一定时机(见下面)下:redolog-buffer写入到文件系统的pagecache(write)–>文件系统pagecache写入到磁盘中(fsnyc),这样的操作的意义是为了提高mysql的吞吐的,具体的机制见下面:
- 一个事务会产生很多条redolog如果每次直接持久化磁盘会消耗大量的磁盘IO,所以redolog会先写入redolog-buffer中,之后在write文件系统的到pagecache中,这俩步是内存操作很快。
- 是否会影响数据的持久化,比如mysql在事务进行中crash了,这时候redolog-buffer中的数据丢失怎么办?答案是由于事物没有提交,所以事物会进行回滚。
- redolog-buffer持久化的条件和机制:
- 受参数innodb_flush_log_at_tx_commit的控制:
- =0:每次提交都只停留在redolog-buffer中;
- =1:每次提交都会持久化到磁盘中;
- =2:每次提交都只会写入到文件的pagecache中;
- innodb后台会有一个线程每秒一次,会把redolog-buffer中的日志,调用write写到pagecahe中,在fsync到磁盘中;
- 其他场景触发redolog的持久化
- 当redolog-buffer占到了innodb_log_buffer_size的一半时候,会调用write将buffer中的log写入到文件系统的pagecache中
- 另一种是并行事物提交时候,如果innodb_flush_log_at_tx_commit设置为1时候会,即时当前事物没有commit,也会将redolog写入到文件系统中。
- 受参数innodb_flush_log_at_tx_commit的控制:
双1设置
即:binlogsnyc=1,innodb_flush_log_at_tx_commit也设置为1。
由于innodb的事物提交redolog和binlog是2PC。
所以当redolog在prepare时候,为了故障恢复一定会持久化一次,所以这时候需要fsync到磁盘中。
binlog在进行fsync到磁盘中
这时候事物在redolog在commit时候,mysql由于有奔溃恢复机制和后台线程每秒轮训一次刷盘会认为redolog没有必要在fsync一次到磁盘了,只会写入到文件的pagecache中。
gourp commit机制
一个事物的提交由于redolog和binglog都要持久化,磁盘IO还是很大Mysql是如何优化这部分呢。这里mysql采用了组提交(group commit)的方式。
如图所示:
- 首先介绍下LSN(log sequence number),一个单调递增的序号,用来对应redolog的一个个写入点。当然也会写入到数据页中,用于flush脏盘时候避免重复执行(不在讨论范围内)
- 如图,当3个事务同时都写完redolog-buffer 并且处于prepare阶段时候,这时候就构成了一个gourp
- 第一个先到达的trx1,成为组里的leader,LSN=50;
- 等trx1开始写盘时候,组里已经有其他俩个事物,这时候LSN=160;
- trx1开始写盘,所有lsn<160的日志都会被写入到磁盘中;
- trx3,trx4就可以直接返回了;
所以在并发事物中,当写完redolog,越晚调用fsync,带的log越多性能也就越好;mysql在这方法采用的是拖时间的策略,即:在双1配置下
redolog-prepare(write)–>binglog(write)–>redolog-prepare(fsync)–>binlog(fsnyc)–>redolog commit(write)
如图:
- 如上图所示,由于redolog在write和fsnyc中有一个binlog-write的过程,所以在持久化磁盘时候你可以带上更多的log;
- 另外:binlog也可以采用组提交,只不过由于这俩个阶段间隔短可能没有redolog那么明显
- binlog的gruop commit的参数如下:
- binlog_group_commit_sync_delay:表示延迟多少微妙后就会调用fsnyc
- binlog_gourp_commit_sync_no_delay_count:表示累计多少次后才调用fsync
- 俩个参数是or的关系,不过如果binlog_group_commit_sync_delay设置为0,binlog_gourp_commit_sync_no_delay_count就无效了
- binlog的gruop commit的参数如下:
综上所述:
- mysql的WAL机制是由于redolog和binlog都是顺序写,保证了高吞吐;
- 同时采用了组提交的方式,来减少了IOPS;