Mysql—Mysql的双一配置保证日志数据不丢失

Posted L-Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mysql—Mysql的双一配置保证日志数据不丢失相关的知识,希望对你有一定的参考价值。

  详细介绍了mysql的双一配置,双一配置可以保证Mysql日志数据不丢失。

  我正在参与CSDN《新程序员》有奖征文,活动地址:https://marketing.csdn.net/p/52c37904f6e1b69dc392234fff425442

  MySQL的"双1"指的是innodb_flush_log_at_trx_commitsync_binlog两个参数都设置为1,这两个参数是控制MySQL 磁盘写入策略以及数据安全性的关键参数

1 write和fsync

  数据从内存持久化到磁盘,通常会需要经历两步:

  1. write:由应用程序调用POSIX file API 中的write()函数将数据从用户进程缓冲区(程序内存)写入磁盘,这一步write()函数实际上仅仅写入到了内核缓冲区(或者说磁盘映射内存os cache,或者说文件系统的page cache),这一块区域仍然属于文件系统向内核申请的一块的内存区域,并没有真正的把数据持久化到磁盘,所以速度是比较快的。
  2. fsync:调用POSIX file API 中的fsync()函数同步的将这些缓存数据真正的持久化到物理磁盘中,这一步完成之后,数据才算真正的完成持久化。一般情况下,我们认为fsync()才占磁盘的IOPS。

  一般情况下,write()函数是应用程序来调用,而当数据在内核缓存区中累积了足够多的数据之后,操作系统会自动fsync()函数同步的将数据真正持久化到磁盘中。这是一种操作系统的“延迟写”特性,这样做的目的是提高性能,但同时也埋下了隐患,在这个过程中,如果服务器宕机,内存中数据将会丢失。因此只有执行同步的刷盘操作才能保证内存的数据一定持久化到了硬盘中,而刷新操作fsync()就是同步阻塞的,直到写完后才返回。

2 innodb_flush_log_at_trx_commit

  事务执行过程中,InnoDB会先把redo log日志写到InnoDB的log buffer内存中。MySQL支持用户自定义在commit(这里的commit指的是sql中的commit,在具体的两阶段提交中对应的prepare阶段)时将log buffer中的日志刷log file中的策略,通过innodb_flush_log_at_trx_commit参数设置:

  1. 设置为0:仅将日志写入log file buffer中。该模式下,在事务提交的时候,不会主动触发写入磁盘的操作,仅依靠InnoDB 的后台线程每秒执行一次刷盘操作,即每秒一次write cache和flush disk
  2. 设置为1:每次事务commit时MySQL都会把log buffer的数据立即写入log file的os cache中,并且立即flush刷到磁盘中去。即每次commit都write cache和flush disk,这是默认设置
  3. 设置为2:每次事务commit时MySQL都会把log buffer的数据写入log file的os cache 缓存,但是刷到flush磁盘的操作并不会同时进行,仅依靠InnoDB 的后台线程每秒执行一次真正的刷盘操作。即每次commit都write cache,每秒一次flush disk

  实际上第一和第三种的情况,是因为InnoDB有一个后台线程,每隔1秒,就会把redo log buffer中的日志,调用write写到文件系统的page cache,然后调用fsync持久化到磁盘。即每秒一次write cache和flush disk
  innodb_flush_log_at_trx_commit设置为0,mysqld进程的崩溃会导致上一秒钟所有事务数据的丢失。当innodb_flush_log_at_trx_commit设置为2,只有在操作系统崩溃或者系统掉电的情况下,上一秒钟所有事务数据才可能丢失(已经写入磁盘缓存中的数据,即使应用崩溃了,操作系统最终还是会将磁盘缓存中的数据持久化到磁盘中)。
  如果把innodb_flush_log_at_trx_commit设置成1,那么所有的redo log事物都不会丢失。那么redo log在prepare阶段就要持久化一次。由于每秒一次后台轮询刷盘,再加上崩溃恢复这个逻辑,InnoDB就认为redo log在commit的时候就不需要fsync了,只会write到文件系统的page cache中就够了,后续等着操作系统的刷盘。

  当然,一个没有提交的事务的内存中的redo log,也是可能已经持久化到磁盘的,有三个地方可以导致:

  1. 如果innodb_flush_log_at_trx_commit=0,那么事务执行中间过程的redo log也是直接写在redo log buffer中的,这些redo log也有可能会被后台线程因为每秒一次的轮询操作一起持久化到磁盘。
  2. redo log buffer占用的空间即将达到 innodb_log_buffer_size一半的时候,后台线程会主动写盘。注意,由于这个事务并没有提交,所以这个写盘动作只是write,而没有调用fsync,也就是只留在了文件系统的page cache。
  3. 并行的事务提交的时候,顺带将这个事务的redo log buffer持久化到磁盘。假设一个事务A执行到一半,已经写了一些redo log到buffer中,这时候有另外一个线程的事务B提交,如果innodb_flush_log_at_trx_commit设置的是1,那么按照这个参数的逻辑,事务B要把redo log buffer里的日志全部持久化到磁盘。这时候,就会带上事务A在redolog buffer里的日志一起持久化到磁盘。

  redo log并没有连续性这个要求,某个事务A在提交前,即使有一部分redo log被事务B提前持久化,但是事务A还是不会进入prepare阶段,该阶段是在事务A自己提交的时候,我们才会走到事务A的redo log prepare这个阶段。

3 sync_binlog

  binlog日志同样有自己的binlog cache,为了保证binlog的连续性,每一个线程都分配了自己的binlog cache,但共用一份binlog 文件。参数binlog_cache_size用于控制单个线程内binlog cache所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。
  binlog 不能“被打断”,主要原因是一个线程只能同时有一个事务在执行。由于这个设定,所以每当执行一个begin/start transaction的时候,就会默认提交上一个事务;如果一个事务的binlog被拆开的时候,在备库执行就会被当做多个事务分段自行,这样破坏了原子性,是有问题的。
  事务执行过程中,会先把日志写到线程自己的binlog cache,事务提交的时候,再把binlog cache写到binlog文件中,随后清空binlog cache。

  sync_binlog参数控制着事务提交时binlog写入磁盘的策略:

  1. sync_binlog 的默认值是0,将二进制日志从binlog cache写入log file的os cache,但是不主动进行flush刷盘操作,而是依赖操作系统来判断什么时候刷盘,若操作系统宕机则会丢失部分二进制日志。
  2. 当sync_binlog =N (N>0) ,MySQL在每写N次二进制日志binary log时,会使用fsync()函数将它同步刷到磁盘中去。

  如果sync_binlog=1,则相当于是同步写入磁盘,保证日志的正确持久化,保证存储下了所有提交的事务,也可以保证主从复制的一致性,但性能最低。
  在sync_logbin = 0或者>1时,很有可能机器出现crash,日志并没有同步到磁盘,重启后,有丢失事务。不能够用来进行数据恢复,对于主从配置也会由于二进制日志的position比备库同步过去的position小,进而造成主从数据不一致情况。

4 总结

  innodb_flush_log_at_trx_commit这个参数设置成1的时候,可以保证MySQL异常重启之后redo log数据不丢失。sync_binlog这个参数设置成1的时候,可以保证MySQL异常重启之后binlog不丢失。
  如果采用"双1"配置,那么MySQL在每一个事务完整commit提交前,需要至少进行两次即时刷盘,一次是 redo log(prepare 阶段),一次是 binlog,此时安全性最高,在mysql服务崩溃或者服务器主机crash的情况下,不会丢失语句和事务。由于每次commot都有两次即时的刷盘操作,导致了大量的IO操作,导致性能急剧下降。
  "双1"设置适合数据安全性要求非常高,而且磁盘IO写能力足够支持业务,比如订单,交易,充值,支付消费系统。双1模式下,当磁盘IO无法满足业务需求时 比如11.11 活动的压力。推荐的做法是 innodb_flush_log_at_trx_commit=2 ,sync_binlog=N (N为500 或1000) 且使用带蓄电池后备电源的缓存cache,防止系统断电异常。
  在主从复制结构中,要保证事务的持久性和一致性,应该使用“双1”配置。

参考资料:
  《 MySQL 技术内幕: InnoDB 存储引擎》
  《高性能 MySQL》
  《MySQL实战45讲 | 极客时间 | 丁奇》

如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!

以上是关于Mysql—Mysql的双一配置保证日志数据不丢失的主要内容,如果未能解决你的问题,请参考以下文章

23 mysql怎么保证数据不丢失?

MySQL系列——MySQL备份和恢复

Linux——MySQL主从复制读写分离

MySQL数据库之——日志和数据库内容的备份与恢复(妈妈再也不用担心数据丢失了)

MySQL主从复制读写分离

mysql设置双1保证数据库一致性