前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >MySQL实战问题02 mysql是如何保证数据不丢失的

MySQL实战问题02 mysql是如何保证数据不丢失的

原创
作者头像
历久尝新
修改2020-06-09 17:17:20
2.1K0
修改2020-06-09 17:17:20
举报
文章被收录于专栏:学而时习之

fa只要保证redolog 和 binlog 持久化到磁盘, 就能保证mysql异常重启后, 数据可以恢复.

binlog与redolog的写入机制

binlog的写入机制

binlog 的写入逻辑比较简单:

  1. 事务执行过程中,先把日志写到 binlog cache,事务提交的时候,再把 binlog cache 写到 binlog 文件中
  2. 一个事务的 binlog 是不能被拆开的,因此不论这个事务多大,也要确保一次性写入
  3. 系统给 binlog cache 分配了一片内存,每个线程一个,参数 binlog_cache_size 用于控制单个线程内 binlog cache 所占内存的大小。如果超过了这个参数规定的大小,就要暂存到磁盘。
  4. 事务提交的时候,执行器把 binlog cache 里的完整事务写入到 binlog 中,并清空 binlog cache

binlog写入中 write 与 fsync

binlog 写盘状态
binlog 写盘状态

上图可看到, 每个线程都有自己的binlog cache, 但是共用一个binlog文件

  • 图中的 write,指的就是指把日志写入到文件系统的 page cache,并没有把数据持久化到磁盘,所以速度比较快
  • 图中的 fsync,才是将数据持久化到磁盘的操作。一般情况下,我们认为 fsync 才占磁盘的 IOPS

write 和 fsync 的时机

由参数sync_binlog控制

  • sync_binlog=0 的时候,表示每次提交事务都只 write,不 fsync;
  • sync_binlog=1 的时候,表示每次提交事务都会执行 fsync;
  • sync_binlog=N(N>1) 的时候,表示每次提交事务都 write,但累积 N 个事务后才 fsync。

redo log的写入机制

事务在执行过程中, 生成redo log 时, 先要写到redo log buffer中

redo log 的三种状态

redo log 的存储状态
redo log 的存储状态
  • 存在 redo log buffer 中,物理上是在 MySQL 进程内存中,就是图中的红色部分;
  • 写到磁盘 (write),但是没有持久化(fsync),物理上是在文件系统的 page cache 里面,也就是图中的黄色部分;
  • 持久化到磁盘,对应的是 hard disk,也就是图中的绿色部分

innodb有一个后台线程, 每隔1s, 就会把redo log buffer 中的日志, 调用write写到文件系统的page cache, 然后调用fsync持久化

控制redolog 的写入策略, 提供了innodb_flush_log_at_trx_commit参数

  • 设置为 0 的时候,表示每次事务提交时都只是把 redo log 留在 redo log buffer 中 ;
  • 设置为 1 的时候,表示每次事务提交时都将 redo log 直接持久化到磁盘;
  • 设置为 2 的时候,表示每次事务提交时都只是把 redo log 写到 page cache。

还有两种会让没有提交事务的redo log 写入到磁盘中

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

组提交(group commit)机制

日志逻辑序列号(LSN):

  • LSN 是单调递增的,用来对应 redo log 的一个个写入点。每次写入长度为 length 的 redo log, LSN 的值就会加上 length。
  • LSN 也会写到 InnoDB 的数据页中,来确保数据页不会被多次执行重复的 redo log

redo log 组提交过程:

如上图, 是三个并发事务(trx1, trx2, trx3)在prepare阶段, 都写完 redolog buffer , 持久化到磁盘的过程, 对应的LSN分别是50, 120, 160

从图中可以看到:

  1. trx1 是第一个到达的,会被选为这组的 leader;
  2. 等 trx1 要开始写盘的时候,这个组里面已经有了三个事务,这时候 LSN 也变成了 160;
  3. trx1 去写盘的时候,带的就是 LSN=160,因此等 trx1 返回时,所有 LSN 小于等于 160 的 redo log,都已经被持久化到磁盘;
  4. 这时候 trx2 和 trx3 就可以直接返回了。

所以:

  1. 一次组提交里面,组员越多,节约磁盘 IOPS 的效果越好
  2. 如果只有单线程压测,那就只能老老实实地一个事务对应一次持久化操作了
  3. 并发更新场景下,第一个事务写完 redo log buffer 以后,接下来这个 fsync 越晚调用,组员可能越多,节约 IOPS 的效果就越好。

两阶段提交细化

写binlog这个步骤实际上是分成两步的

  1. 先把binlog从 binlog cache 中写到磁盘上binlog文件;
  2. 调用fsync持久化

mysql为了让组提交的效果更好, 实际步骤如下:

这么一来, binlog也可以组提交, 在执行上图第四步把binlog fsyn到磁盘的时候, 如果多个食物binlog已经写完, 也是一起持久化的.这样也可以减少iops的消耗

不过通常情况下第 3 步执行得会很快,所以 binlog 的 write 和 fsync 间的间隔时间短,导致能集合到一起持久化的 binlog 比较少,因此 binlog 的组提交的效果通常不如 redo log 的效果那么好。

如何提高binlog 组提交效果

可以通过设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 来实现

  1. binlog_group_commit_sync_delay 参数,表示延迟多少微秒后才调用 fsync;
  2. binlog_group_commit_sync_no_delay_count 参数,表示累积多少次以后才调用 fsync。

这两个条件是或的关系,也就是说只要有一个满足条件就会调用 fsync。

所以,当 binlog_group_commit_sync_delay 设置为 0 的时候,binlog_group_commit_sync_no_delay_count 也无效了。

WAL机制得益于两个方面

  1. redo log 和 binlog 都是顺序写,磁盘的顺序写比随机写速度要快;
  2. 组提交机制,可以大幅度降低磁盘的 IOPS 消耗。

一些问题:

如果你的 MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?

  1. 设置 binlog_group_commit_sync_delay 和 binlog_group_commit_sync_no_delay_count 参数,减少 binlog 的写盘次数。这个方法是基于“额外的故意等待”来实现的,因此可能会增加语句的响应时间,但没有丢失数据的风险
  2. 将 sync_binlog 设置为大于 1 的值(比较常见是 100~1000)。这样做的风险是,主机掉电时会丢 binlog 日志
  3. 将 innodb_flush_log_at_trx_commit 设置为 2。这样做的风险是,主机掉电的时候会丢数据

执行一个 update 语句以后,我再去执行 hexdump 命令直接查看 ibd 文件内容,为什么没有看到数据有改变呢?

这可能是因为 WAL 机制的原因。update 语句执行完成后,InnoDB 只保证写完了 redo log、内存,可能还没来得及将数据写到磁盘。

事务执行期间,还没到提交阶段,如果发生 crash 的话,redo log 肯定丢了,这会不会导致主备不一致呢?

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

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

这么设计的主要原因: binlog是不能被打断的. 一个事务的binlog必须连续写, 因此要整个事务完成之后, 再一起写到文件里.

而redo log 并没有这个要求, 中间有生成的日志可以写到redo log buffer 中, redolog buffer中的内容还可以"搭便车", 同其他事务提交的时候可以被一起写到磁盘中.

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • binlog与redolog的写入机制
    • binlog的写入机制
      • binlog 的写入逻辑比较简单:
      • binlog写入中 write 与 fsync
      • write 和 fsync 的时机
    • redo log的写入机制
      • redo log 的三种状态
      • 控制redolog 的写入策略, 提供了innodb_flush_log_at_trx_commit参数
      • 还有两种会让没有提交事务的redo log 写入到磁盘中
  • 组提交(group commit)机制
    • 日志逻辑序列号(LSN):
      • redo log 组提交过程:
        • 两阶段提交细化
          • 如何提高binlog 组提交效果
            • WAL机制得益于两个方面
            • 一些问题:
              • 如果你的 MySQL 现在出现了性能瓶颈,而且瓶颈在 IO 上,可以通过哪些方法来提升性能呢?
                • 执行一个 update 语句以后,我再去执行 hexdump 命令直接查看 ibd 文件内容,为什么没有看到数据有改变呢?
                  • 事务执行期间,还没到提交阶段,如果发生 crash 的话,redo log 肯定丢了,这会不会导致主备不一致呢?
                    • 为什么 binlog cache 是每个线程自己维护的,而 redo log buffer 是全局共用的?
                    相关产品与服务
                    云数据库 MySQL
                    腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档