暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL学习之如何保证数据不丢失的

白砂 2021-07-06
723

在之前学习了WAL机制之后,得到的结论就是: 只要redo log 和 binlog 保证持久化到磁盘, 就能确保MySQL异常重启后, 数据可以恢复。

我们知道使用binlog做数据的恢复、搭建主从架构的集群时,有没有想过,我们使用的binlog是安全的?数据真的是没有丢失的吗?

redo log 及时数据库发生异常重启, 之前的数据也不会消失,怎么确保也是安全的呢?数据真的是没有丢失的吗?

binlog 的写入机制

首先先了解一下binlog cache (我看到有的地方称之为 “binlog的高速缓存”)

意思就是说:所有未进行 commit 的事务产生的 binlog,都会被先记录到 binlog cache 中。事务 commit 的时候,再把 binlog cache 的数据写到 binlog 日志文件中,并清空binlog cache。

一个事务的 binlog 是不能被拆开的,一个事务不管有多大,都要一次性写入,这里就会出现一个 binlog cache 的存储问题。至于为什么不能拆开写入,请耐心往下看。

系统会给 binlog cache 分配一块内存,每个线程都会有一个,参数 binlog_cache_size 是用于控制单个线程内 binlog cache 所占内存的大小。如果所占内存超过了 binlog_cache_size 的大小, 就要暂存到磁盘。

写盘流程

从图中可以看到,每个线程有自己的 binlog cache,但是共用一份 binlog 文件。

  • write 指的是文件系统(OS) 的 page cache,并没有把数据持久化到磁盘,所以速度比较快。
  • fsync 指的是将数据持久化到磁盘。

write 和 fsync 的时机,是由参数 sync_binlog 控制的:

  • sync_binlog = 0时,表示每次提交事务只是写入到 os 的 page cache,不会进行持久化磁盘。
  • sync_binlog = 1时,表示每次提交事务都会进行持久化磁盘操作。
  • sync_binlog = N(N>1)时,表示每次提交事务都会进行写入到 page cache,积累N个事务后在进行持久化到磁盘。

注:sync_binlog = N时,如果发生异常宕机,会丢失最近N个事务的 binlog 日志

redo log 的写入机制

redo log 在进行写入的时候, 实现写入到 redo log buffer 的,如果事务执行期间MySQL发生异常重启,这部分日志即使丢了也是不会有任何损失的,因为事务还没有进行提交。

日志写到 redo log buffer 是很快的,写入到 os 的 page cache 也差不多,但是持久化到磁盘就会很慢了,为了控制 redo log 的写入策略,innodb_flush_log_trx_commit 参数.

  • 设置为 0 时,表示事务提交时写入到 redo log buffer 中;
  • 设置为 1 时,表示事务提交时写入到磁盘中;
  • 设置为 2 时,表示事务提交是写入到 os 的 page cache 中。

innodb 有个进程会每隔1秒进行刷盘操作,将 redo log buffer 中的日志先写到page cache,然后在持久化到磁盘中。

注:事务执行过程中的(没有提交的)事务redo log 也是写入到 redo log buffer 的,这些 redo log 有可能会被后台进程一起持久化到磁盘中。

除了后台进程进行刷盘外,还有两种场景会导致没有提交的事务被持久化。

  1. redo log buffer 占用空间即将到达 innodb_log_buffer_size 一半的时候,后台进程会进行主动写入到 os 的 page cache 中。
  2. 并行事务进行提交的时候,会顺带将事务的 redo log buffer 持久化到磁盘。如果此时一个事务A执行到一半,已经写了一些 redo log 到 buffer 中,这时有一个事务B进行提交,如果innodb_flush_log_trx_commit 设置的是1(事务提交时写入到磁盘)时,那么按照这个逻辑,事务B会把 redo log buffer 里的日志全部持久化到磁盘

还记得两阶段提交吗?redo log 先进行 prepare ,binlog 提交,最后 redo log 提交。如果设置的是事务提交时写入到磁盘时,在 redo log 是 prepare 阶段时,也要进行一次持久化。

因为有一个崩溃恢复的逻辑是依赖于 prepare 的 redo log 和 binlog 来恢复数据的。

这就是MySQL的双1配置,指的就是 sync_binlog 和 innodb_flush_log_at_trx_commit 都设置为1, 也就是事务提交时都持久化到磁盘。一个事务提交前,要等待两次刷盘,redo log 的 prepare 和 binlog 的 commit。

问题

  1. 执行一个 update 语句之后, 我再去执行 hexdump 命令查看ibd 文件内容, 为什么数据没有变化?

答: 这是WAL机制的原因。update执行完成之后, InnoDB 只保证写完了redo log、内存,可能还没有来及将数据持久化到磁盘中。


  1. 为什么binlog是每个线程自己维护的,而redo log是全局共用的?

答:MySQL这样设计是因为 binlog 是不能被打断的
。一个事务的binlog必须连续写,binlog 是一种逻辑性日志,记录的是一个事务完整的语句。当用来做主从同步,如果是分散写的话,可能会造成事务不完整,出现原子性问题。

而redo log是属于物理性日志,记录的是基于数据页的,一个事务可以发生很多数据页的记录,只要保证redo log 数据页是完整的就可以。


  1. 如果事务在执行阶段,发生了crash,redo log 肯定丢了,这会不会导致主备不一致?

答:不会。因为binlog还在binlog cache 里,没有发送给备库。crash 以后的redo log 和 binlog 都没有了,从业务角度来讲这个事务没有提交,所以数据是一致的。


如果我的文章对你有所帮助,还请帮忙点赞、在看、转发一下,你的支持会激励我继续坚持下去,非常感谢!

你还可以把我的公众号设为「星标」,这样当公众号文章更新时,你会在第一时间收到推送消息,避免错过我的文章更新。

文章转载自白砂,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论