1、更新语句的执行流程

创建表 T:

1
mysql> create table T(ID int primary key, c int);

将 ID = 2 这一行的值加 1 的 SQL 语句:

1
mysql> update T set c=c+1 where ID=2;

查询语句的那一套流程,更新语句也是同样会走一遍。

查询执行流程

经过连接器查询缓存之后,分析器通过词法和语法分析知道这是一条更新语句,优化器决定要使用 ID 这个索引,执行器负责具体执行,找到这一行,然后更新。

与查询流程不一样的是,更新流程还涉及两个重要的日志模块: **redo log(重做日志)**和 binlog(归档日志)

2、redo log

redo log 是 InnoDB 引擎特有的日志模块。

在 MySQL 中,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了提高更新效率,MySQL 使用 WAL(Write-Ahead Logging)技术,它的关键点就是先写日志,再写磁盘。

当有记录需要更新时,InnoDB 引擎就会先把记录写到 redo log 里面,并更新内存,这时更新就算完成了。同时,InnoDB引擎会在适当的时候(系统比较空闲),将这个记录更新到磁盘里面。

InnoDB的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么总共可以记录 4GB 的操作。从头开始写,写到末尾又回到开头循环写。

redo log 写

write pos 是当前记录的位置,一边写一边后移。 checkpoint 是当前要擦除的位置,擦除记录前要把记录更新到数据文件。

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

3、binlog

binlog 是 Sever 层的日志模块,只能用于归档,比如主从复制。

两种日志不同:

  1. redo log 是InnoDB引擎特有的;binlog是MySQL的Server层实现的,所有引擎都可以使用。
  2. redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID=2这一行的c字段加1”。
  3. redo log 是循环写,写到末尾又回到开头写;binlog是追加写入,写到一定大小后会切换到下一个,并不会覆盖以前的日志。

4、两阶段提交

上面简单的 updata 语句的执行流程。

update 执行流程

  1. 执行器先找引擎取 ID = 2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。

  2. 执行器拿到引擎给的行数据,把这个值加上1,比如原来是N,现在就是N+1,得到新的一行数据,再调用引擎接口写入这行新数据。

  3. 引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。

  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。

  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成 commit 状态,更新完成。

如果不使用两阶段提交,会有什么问题?

  1. 先写 redo log 后写 binlog。假设在 redo log 写完,binlog 还没有写完的时候,MySQL进程异常重启,binlog 中没有更新的数据。

  2. 先写 binlog 后写 redo log。如果在 binlog 写完之后 crash,由于 redo log 没有写,崩溃恢复以后这个事务无效,所以这一行 c 的值是 0,但 binlog 中 c = 1 了。

5、配置

双1设置:

innodb_flush_log_at_trx_commit = 1 表示每次事务的 redo log 都直接持久化到磁盘。这样可以保证 MySQL 异常重启之后数据不丢失。

sync_binlog = 1 表示每次事务的 binlog 都持久化到磁盘。这样可以保证 MySQL 异常重启之后 binlog 不丢失。

6、问题

在什么场景下,一天一备会比一周一备更有优势呢?

好处是“最长恢复时间”更短。

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

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

0%