hincky的主页 hincky的主页
  • 2023

    • nginx
    • prometheus
    • 小工具
    • 部署
  • 数据库

    • MySQL是怎么使用/运行的
    • Redis核心技术与实战
  • 极客时间

    • Web协议详解与抓包实战
    • SQL必知必会
    • MySQL45讲
个人日常
  • 分类
  • 标签
  • 归档
  • 随笔
GitHub (opens new window)

Hincky

当有趣的人,做想做的事
  • 2023

    • nginx
    • prometheus
    • 小工具
    • 部署
  • 数据库

    • MySQL是怎么使用/运行的
    • Redis核心技术与实战
  • 极客时间

    • Web协议详解与抓包实战
    • SQL必知必会
    • MySQL45讲
个人日常
  • 分类
  • 标签
  • 归档
  • 随笔
GitHub (opens new window)
  • 编程语言

  • 数据库

  • 极客时间

    • Web协议详解与抓包实战

    • GO语言第一课

    • MySQL45

      • 查询语句如何执行
        • 连接器
          • 连接问题
        • 查询缓存
        • 分析器
          • 词法分析
          • 语法分析
        • 优化器
        • 执行器
        • 本篇命令
      • 更新语句如何执行
        • redo log
          • crash-safe
          • redo log特点
        • binlog
          • binlog特点
        • 数据恢复过程
          • 两段提交
          • 先redo后bin
          • 先bin后redo
        • 总结
          • 预防mysql异常重启
          • 如何选择备份周期
      • 事务隔离
        • 隔离级别
          • 读提交 和 可重复读
          • 不同隔离级别 视图创建差异
          • Oracle迁移事项
          • 可重复度的场景
        • 事务隔离的实现
          • 如何避免长事务
        • 事务启动方式
      • 索引入门
        • 常见索引模型
          • 哈希KV表
          • 有序数组
          • 搜索树
          • 数据库常用N叉树
        • InnoDB索引模型-B+树
          • 主键索引和普通索引区别
    • SQL必知必会

    • Linux性能优化实战

  • 读书笔记
  • 极客时间
  • MySQL45
hincky
2022-11-09
目录

更新语句如何执行

与查询流程不一样的是,更新流程还涉及两个重要的日志模块

  • redo log(重做日志)
  • binlog(归档日志)

# redo log

redo log 是 InnoDB 引擎特有的日志

当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log(粉板)里面,并更新内存,这个时候更新就算完成了。

同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做,这就像打烊以后掌柜将粉板账目抄到账本上。

InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。

write pos 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。checkpoint 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。

write pos 和 checkpoint 之间的是“粉板”上还空着的部分,可以用来记录新的操作。如果 write pos 追上 checkpoint,表示“粉板”满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。

# crash-safe

有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。

innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。

# 0 该模式速度最快,但不太安全,mysqld进程的崩溃会导致上一秒钟所有事务数据的丢失
# 1 该模式最安全,也是最慢的一种方式。在mysqld服务崩溃或者服务器主机crash下,binary log 只有可能丢失最多一个语句或一个事务。
# 2 该模式速度较快,也比0安全,只有在操作系统崩溃或者系统断电的情况下,上一秒钟所有事务数据才可能丢失。
set global innodb_flush_log_at_trx_commit=1
1
2
3
4

# redo log特点

  • InnoDB 引擎特有的
  • 物理日志,记录的是“在某个数据页上做了什么修改”
  • 循环写的,空间固定会用完

# binlog

binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。

update 语句时的内部流程

  1. 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,进行修改(将N变为N+1),再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存中,同时将该更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成提交(commit)状态,更新完成。

图中浅色框表示是在 InnoDB 内部执行的,深色框表示是在执行器中执行的。

# binlog特点

  • binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  • binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”
  • binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志

# 数据恢复过程

备份系统中一定会保存binlog中记录所有的逻辑操作

系统会定期做整库备份。这里的“定期”取决于系统的重要性,可以是一天一备,也可以是一周一备。

当需要恢复到指定的某一秒时,比如某天下午两点发现中午十二点有一次误删表,需要找回数据,那你可以这么做:

  • 首先,找到最近的一次全量备份,如果你运气好,可能就是昨天晚上的一个备份,从这个备份恢复到临时库;
  • 然后,从备份的时间点开始,将备份的 binlog 依次取出来,重放到中午误删表之前的那个时刻。

这样你的临时库就跟误删之前的线上库一样了,然后你可以把表数据从临时库取出来,按需要恢复到线上库去。

# 两段提交

为了让两份日志之间的逻辑一致

由于 redo log 和 binlog 是两个独立的逻辑,如果不用两阶段提交,要么就是先写完 redo log 再写 binlog,或者采用反过来的顺序

# 先redo后bin

假设在 redo log 写完,binlog 还没有写完的时候,MySQL 进程异常重启。由于我们前面说过的,redo log 写完之后,系统即使崩溃,仍然能够把数据恢复回来,所以恢复后这一行 c 的值是 1。

但是由于 binlog 没写完就 crash 了,这时候 binlog 里面就没有记录这个语句。因此,之后备份日志的时候,存起来的 binlog 里面就没有这条语句。然后你会发现,如果需要用这个 binlog 来恢复临时库的话,由于这个语句的 binlog 丢失,这个临时库就会少了这一次更新,恢复出来的这一行 c 的值就是 0,与原库的值不同。

# 先bin后redo

如果在 binlog 写完之后 crash,由于 redo log 还没写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0。但是 binlog 里面已经记录了“把 c 从 0 改成 1”这个日志。所以,在之后用 binlog 来恢复的时候就多了一个事务出来,恢复出来的这一行 c 的值就是 1,与原库的值不同。

# 总结

# 预防mysql异常重启

redo log

innodb_flush_log_at_trx_commit 这个参数设置成 1 的时候,表示每次事务的 redo log 都直接持久化到磁盘。这个参数我建议你设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。

bin log

sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘。这个参数我也建议你设置成 1,这样可以保证 MySQL 异常重启之后 binlog 不丢失。

# 0 该模式速度最快,但不太安全,mysqld进程的崩溃会导致上一秒钟所有事务数据的丢失
# 1 该模式最安全,也是最慢的一种方式。在mysqld服务崩溃或者服务器主机crash下,binary log 只有可能丢失最多一个语句或一个事务。
# 2 该模式速度较快,也比0安全,只有在操作系统崩溃或者系统断电的情况下,上一秒钟所有事务数据才可能丢失。
set global innodb_flush_log_at_trx_commit=1

# 将binlog二进制日志同步到磁盘
set global sync_binlog=1
1
2
3
4
5
6
7

# 如何选择备份周期

究竟是一天一备,还是一周一备

根据RTO(恢复目标时间)和存储空间来取舍

一天一备 一周一备
RTO 短 长
占用存储空间 大 小

举例

在一天一备的模式里,最坏情况下需要应用一天的 binlog。比如,你每天 0 点做一次全量备份,而要恢复出一个到昨天晚上 23 点的备份。

一周一备最坏情况就要应用一周的 binlog 了。

编辑 (opens new window)
#mysql
查询语句如何执行
事务隔离

← 查询语句如何执行 事务隔离→

最近更新
01
集成chatgpt的工具
05-24
02
修改服务器ssh默认连接端口
05-22
03
阿里云免费证书
05-15
更多文章>
Theme by Vdoing | Copyright © 2022-2023 Hincky | MIT License | 粤ICP备2022120427号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式