MySQL -- 数据可靠性

栏目: 数据库 · 发布时间: 5年前

  1. 事务在执行过程中,先把日志写到 binlog cache ,事务 提交 时,再把 binlog cache 写到 binlog file
  2. 一个事务的binlog是 不能被拆开 的,不论事务多大,也要确保 一次性写入
  3. 系统会给 每个线程 分配一块内存 binlog cache ,由参数 binlog_cache_size 控制
    • 如果超过了 binlog_cache_size ,需要 暂存到磁盘
  4. 事务提交时,执行器把 binlog cache 里面的 完整事务 写入到 binlog file ,并 清空 binlog cache
-- 2097152 Bytes = 2 MB
mysql> SHOW VARIABLES LIKE '%binlog_cache_size%';
+-----------------------+----------------------+
| Variable_name         | Value                |
+-----------------------+----------------------+
| binlog_cache_size     | 2097152              |
| max_binlog_cache_size | 18446744073709547520 |
+-----------------------+----------------------+

写入过程

MySQL -- 数据可靠性

  1. 每个线程都有自己的 binlog cache ,但共用一份 binlog file
  2. write :把日志写入到 文件系统的page cache ,但并没有将数据持久化到磁盘, 速度比较快
  3. fsync :将数据持久化到磁盘, fsync 才会占用磁盘的 IOPS

sync_binlog

mysql> SHOW VARIABLES LIKE '%sync_binlog%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog   | 0     |
+---------------+-------+
  1. sync_binlog=0 ,每次提交事务都只 write ,不 fsync
  2. sync_binlog=1 ,每次提交事务都会执行 fsync
  3. sync_binlog=N ,每次提交事务都会 write ,但累计N个事务后才 fsync
    • 一般为 (100 ~ 1,000) ,可以 提高性能
    • 如果主机 断电 ,会 丢失最近的N个事务的binlog

redolog的写入机制

  1. 事务在执行过程中,生成的 redolog 需要先写到 redolog buffer
  2. redolog buffer 里面的内容,并不需要 每次 生成后都直接持久化到磁盘
    • 如果事务执行期间,MySQL异常重启,那么这部分日志丢失了
    • 由于事务 没有提交 ,所以这时的日志丢失不会有什么影响
  3. 在事务还未提交时, redolog buffer 中的 部分日志 也是有可能 持久化到磁盘

redolog的状态

MySQL -- 数据可靠性

  1. 红色部分:存在于 redolog buffer 中,物理上存在于 MySQL进程的内存
  2. 黄色部分:写到磁盘( write ),但没有持久化( fsync ),物理上存在于 文件系统的page cache
  3. 绿色部分:持久化到磁盘,物理上存在于 hard disk
  4. 日志写到 redolog buffer 是很快的, writeFS page cache 也比较快,但 fsync 到磁盘的速度就会慢很多

redolog的写入策略

事务提交

mysql> show variables like '%innodb_flush_log_at_trx_commit%';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 2     |
+--------------------------------+-------+
  1. innodb_flush_log_at_trx_commit=0
    • 每次事务提交时都只是将 redolog 写入到 redolog buffer红色部分
    • redolog 只存在内存中,MySQL本身异常重启也会丢失数据, 风险太大
  2. innodb_flush_log_at_trx_commit=1
    • 每次事务提交时都将 redolog 持久化到磁盘( 绿色部分
    • 两阶段提交: redolog prepare -> 写binlog -> redolog commit
    • redolog prepare 需要持久化一次,因为崩溃恢复依赖于 prepareredologbinlog
    • redolog commit 就不需要持久化( fsync )了,只需要 writeFS page cache 即可
    • 双1配置 :一个事务完整提交前,需要 2次刷盘redolog prepare + binlog
      • 优化: 组提交
  3. innodb_flush_log_at_trx_commit=2
    • 每次事务提交时都将 redolog 写入到 FS page cache黄色部分

后台刷新

  1. 后台线程:每隔 1秒 ,就会将 redolog buffer 中的日志,调用 write 写入到 FS page cache ,再调用 fsync 持久化到磁盘
  2. 事务执行期间的 redolog 是直接写到 redolog buffer ,这些 redolog 也会被后台线程一起持久化到磁盘
    • 即一个 未提交 的事务的 redolog 也是有可能已经持久化到磁盘的

事务未提交

-- 16777216 Bytes = 16 MB
mysql> SHOW VARIABLES LIKE '%innodb_log_buffer_size%';
+------------------------+----------+
| Variable_name          | Value    |
+------------------------+----------+
| innodb_log_buffer_size | 16777216 |
+------------------------+----------+
  1. redolog buffer 占用的空间即将达到 innodb_log_buffer_size一半 时,后台线程会 主动写盘
    • 由于事务尚未提交,因此这个写盘动作是在 write ,不会调用 fsync ,停留在 FS page cache
  2. 并行事务提交 时,顺带将 未提交事务redolog buffer 持久化到磁盘
    • 事务A执行到一半,有部分 redologredolog buffer
    • 事务B提交,且 innodb_flush_log_at_trx_commit=1 ,事务B要把 redolog buffer 里面的日志 全部 持久化到磁盘
    • 这时会带上事务A在 redolog buffer 里的日志一起持久化到磁盘

组提交

LSN

  1. LSN :log sequence number
  2. LSN单调递增 的,对应 redolog写入点
    • 每次写入长度为length的 redologLSN 就会加上length
  3. LSN 也会写入到 数据页 中,用来 确保数据页不会被多次执行重复的 redolog

样例

MySQL -- 数据可靠性

  1. 三个 并发事务 处于 prepare 阶段: tx1tx2tx3
    • 都完成写入 redolog buffer持久化到磁盘 的过程
    • 对应的 LSN50120160
  2. tx1 第一个到达,被选为组 leader
  3. trx1 要开始 写盘 的时候,组内已经有3个事务, LSN 变成了 160
  4. trx1 带着 LSN=160 去写盘,等 trx1 返回时,所有 LSN<160redolog 都已经持久化到磁盘
    • trx2trx3 可以 直接返回

小结

  1. 一次组提交里面,组员越多,节省磁盘的IOPS的效果越好
  2. 并发更新 场景下,第1个事务写完 redolog buffer 后,接下来的 fsync 越晚调用,节省磁盘的IOPS的效果越好

binlog组提交

MySQL -- 数据可靠性 写binlog 其实分两步: binlog cache -> (write) -> binlog file + binlog file -> (fsync) -> disk

MySQL为了让组提交效果更好,延后了 fsync 执行时机,两阶段提交细化如下

MySQL -- 数据可靠性

binlog 也可以支持 组提交 了,但第3步执行很快,导致了 binlog 的组提交效果不如 redolog 的组提交效果

参数

mysql> SHOW VARIABLES LIKE '%binlog_group_commit_sync%';
+-----------------------------------------+-------+
| Variable_name                           | Value |
+-----------------------------------------+-------+
| binlog_group_commit_sync_delay          | 0     |
| binlog_group_commit_sync_no_delay_count | 0     |
+-----------------------------------------+-------+
  1. binlog_group_commit_sync_delay :延迟多少 微秒 才调用 fsync
  2. binlog_group_commit_sync_no_delay_count :累计多少次之后才调用 fsync
  3. 两者关系: ,但当 binlog_group_commit_sync_delay=0 时, binlog_group_commit_sync_no_delay_count 无效

WAL性能

  1. redologbinlog 都是 顺序写
  2. 组提交机制 :可以大幅降低磁盘的 IOPS消耗

MySQL的IO瓶颈

  1. 设置 binlog_group_commit_sync_delaybinlog_group_commit_sync_no_delay_count
    • 故意等待 ,利用 组提交 减少 IOPS 消耗,同时可能会 增加语句的响应时间 ,但 没有丢数据风险
  2. sync_binlog=N(100~1,000)
    • 主机 断电 会丢失 binlog 日志
  3. innodb_flush_log_at_trx_commit=2
    • 主机 断电 会丢失数据
    • 20性能接近 ,但设置为 0 (数据仅在 redolog buffer ),在MySQL 异常重启 时也会丢失数据

crash-safe的保证

  1. 如果客户端收到 事务成功 的消息,事务就一定持久化了的
  2. 如果客户端收到 事务失败 (主键冲突、回滚等)的消息,事务一定是失败的
  3. 如果客户端收到 执行异常 的消息,应用需要 重连 后通过 查询 当前状态来继续后续的逻辑
    • 数据库只需要保证内部( 数据与日志之间主从之间 )一致即可

参考资料

《MySQL实战45讲》

转载请注明出处:http://zhongmingmao.me/2019/02/21/mysql-reliability/

访问原文「MySQL -- 数据可靠性」获取最佳阅读体验并参与讨论


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Looking For a Challenge

Looking For a Challenge

the University of Warsaw / the University of Warsaw / 2012-11 / 0

LOOKING FOR PROGRAMMING CHALLENGES? Then this book is for you! Whether it's the feeling of great satisfaction from solving a complex problem set, or the frustration of being unable to solve a task,......一起来看看 《Looking For a Challenge》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具