Mysql是如何保证数据不丢失的

Mysql是如何保证数据不丢失的

binlog

  1. 事务执行过程中binlog写入到binlog cache,事务提交时写入到binlog文件中。
  2. 事务的原子性决定这无论事务有多大,binlogcahe都要一次性完整的写入到binlog文件中,写入方式如下:
    1. 系统为每个binlog_cache开辟了一片内存(每个线程都有一个),参数是blong_cache_size,超过这个阈值的会保存在临时文件中
    2. 事务提交时候,出于性能的考虑回将bionlog_cache都write到文件系统的page_cache中,在通过fsync刷新到磁盘文件中。如下图所示是binlog的同步机制。

如图:
avatar

  • 只有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的吞吐的,具体的机制见下面:

avatar

  1. 一个事务会产生很多条redolog如果每次直接持久化磁盘会消耗大量的磁盘IO,所以redolog会先写入redolog-buffer中,之后在write文件系统的到pagecache中,这俩步是内存操作很快。
  2. 是否会影响数据的持久化,比如mysql在事务进行中crash了,这时候redolog-buffer中的数据丢失怎么办?答案是由于事物没有提交,所以事物会进行回滚。
  3. redolog-buffer持久化的条件和机制:
    1. 受参数innodb_flush_log_at_tx_commit的控制:
      1. =0:每次提交都只停留在redolog-buffer中;
      2. =1:每次提交都会持久化到磁盘中;
      3. =2:每次提交都只会写入到文件的pagecache中;
    2. innodb后台会有一个线程每秒一次,会把redolog-buffer中的日志,调用write写到pagecahe中,在fsync到磁盘中;
    3. 其他场景触发redolog的持久化
      1. 当redolog-buffer占到了innodb_log_buffer_size的一半时候,会调用write将buffer中的log写入到文件系统的pagecache中
      2. 另一种是并行事物提交时候,如果innodb_flush_log_at_tx_commit设置为1时候会,即时当前事物没有commit,也会将redolog写入到文件系统中。

双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)的方式。
如图所示:
avatar

  • 首先介绍下LSN(log sequence number),一个单调递增的序号,用来对应redolog的一个个写入点。当然也会写入到数据页中,用于flush脏盘时候避免重复执行(不在讨论范围内)
  • 如图,当3个事务同时都写完redolog-buffer 并且处于prepare阶段时候,这时候就构成了一个gourp
    1. 第一个先到达的trx1,成为组里的leader,LSN=50;
    2. 等trx1开始写盘时候,组里已经有其他俩个事物,这时候LSN=160;
    3. trx1开始写盘,所有lsn<160的日志都会被写入到磁盘中;
    4. trx3,trx4就可以直接返回了;

所以在并发事物中,当写完redolog,越晚调用fsync,带的log越多性能也就越好;mysql在这方法采用的是拖时间的策略,即:在双1配置下
redolog-prepare(write)–>binglog(write)–>redolog-prepare(fsync)–>binlog(fsnyc)–>redolog commit(write)

如图:
avatar

  • 如上图所示,由于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就无效了

综上所述:

  • mysql的WAL机制是由于redolog和binlog都是顺序写,保证了高吞吐;
  • 同时采用了组提交的方式,来减少了IOPS;