在上篇文章中我们了解了一个查询语句是怎么执行的,并了解了执行过程中涉及到的一些处理模块。一条查询 SQL 的执行过程需要经过连接器、分析器、优化器、执行器等模块,最后到达存储引擎。
一条更新语句的执行流程是怎样的呢?我们还是从一个更新 SQL 说起,如下是一个表的创建语句,表有一个主键 ID 和一个 int 类型字段 num:
CREATE TABLE test(ID int primary key, num int 10);
如果要将 ID 为 1 的数据 num 加 1,SQL 语句如下:
UPDATE test SET num = num + 1 WHERE ID = 1;
首先,可以肯定的说,查询 SQL 语句的流程,更新语句同样会走一遍,如下图
在前面说过,当一个表被更新的时候,跟这个表相关的查询缓存会失效,所以这条语句就会把表 test 上所有缓存结果清空。这也就是我们不建议使用查询缓存的原因
与查询不同的是,更新涉及了两个重要的日志模块 redo log 和 bin log
redo log 使用了 WAL(Write-Ahead-Logging) 技术,也就是先写日志,然后再写磁盘。
redo log 用来保证 crash-safe 能力,
innodb_flush_log_at_trx_commit
参数为 1 的时候,每次事务的 redo log 都会持久化到磁盘中
在一条记录需要更新的时候,InnoDB 引擎会先将记录写到 redo log 中,并更新内存,这个时候就已经更新完成了。同时,InnoDB 会在适当时间将 操作记录更新到磁盘中
当然,redo log 也不可能无限制的追加,它的大小是固定的,例如可以配置为一组文件 innodb_log_file_in_group=4, innodb_log_file_size=4294967292
,每个文件大小为 1 GB,总共可以记录 4 GB 的操作,从头开始循环写,如下图所示:
write pos 为当前记录的位置,边写边向后移动,写到最后一个文件后就回到第一个文件开头。check point 是当前要擦除的位置,移动顺序与写一致,擦除前需要把记录文件更新到数据文件中。
write pos 和 check point 中间部分可以用来记录新的操作。如果 write 追上了 check 的话,不能再执行新的更新操作,需要先等待擦除掉一些记录。
有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录也不会丢失,这个能力称之为 crash-safe
redo log 是 InnoDB 引擎特有的日志,server 层的日志是 bin log(归档日志)
sync_binlog
参数设置为 1,每次事务的 bin log 都会持久化到磁盘
MySQL 开始是没有 InnoDB 引擎的,自带的引擎是 MyISAM,MyISAM 没有 carsh-safe 的能力,bin log 日志只能用于归档。InnoDB 是另一个公司以插件形式引入 MySQL 的,所以 InnoDB 使用另外一套日志系统 redo log 来实现 crash-safe 能力
UPDATE test SET num = num + 1 WHERE ID = 1;
SQL 执行流程如下图,浅色框表示在 InnoDB 中执行,深色表示在执行器中执行的
bin log 会记录所有的逻辑操作,并且采用追加写的形式,如果要将数据恢复到指定的时间,可以这么做:
redo log 和 bin log 是两个独立的逻辑,我们来看看如果先写一个再写另一个可能会出现什么问题
SQL 语句如下:
UPDATE test SET num = num + 1 WHERE ID = 1;
先写 redo log 后写 bin log
如果 redo log 写完了,bin log 还没有写完的话,MySQL 进程异常重启,虽然系统崩溃了我们仍然能用 redo log 把数据恢复过来,但是由于 bin log 还没有写就崩了,里面没有记录这个语句,导致备份日志里面没有这个语句。如果我们要用 bin log 来恢复临时库的话,就会少了这一次更新操作,恢复的数据会与源数据库的值不同
先写 bin log 再写 redo log
如果在 bin log 写完了就崩了的话,因为 redo log 还没写,崩溃恢复后这个事务没生效,这一行的值还是原来的值没有变化,但是 bin log 里面已经记录了更新的日志,以后用 bin log 来恢复的话就多了一个事务,恢复的这行与源数据库的值不同。
redo log 和 bin log 都用来记录事务的提交状态,两阶段提交就是为了保持这两个状态的一致。
如果不使用两阶段提交的话,数据库状态就可能与用日志恢复的库状态不一致。我们对数据库进行扩容用 bin log 来进行备库的搭建的时候就会导致主从数据库不一致的情况发生
提交过程中 MySQL 崩溃
做的什么修改
,bin log 是逻辑日志,记录的是 SQL 的原始逻辑提供回滚和多个行版本控制(MVCC)
数据修改的时候不仅记录了 redo log,还有相对应的 undo log,如果某些原因导致事务失败或者回滚了,可以借助 undo log 来回滚
辅助 redo log 实现事务持久性
和 redo log 不同的是,undo log 是逻辑日志,当修改一条记录的时候,undo log 会记录一条相反的原来数据的 update 记录,删除操作则会记录一条原来数据的 insert 记录
当 rollback 的时候,可以从 undo log 的记录中读取到响应内容并进行回滚。
undo log 采用 segment 方式记录,事务提交时,InnoDB 不会立即删除 undo log,如果隔离级别为 repeatable read (可重复读)时,事务读取的都是开启事务时的最新提交行版本,事务不结束,就不能删除。事务提交时会将事务对应的 undo log 放到删除列表中,未来通过 purge 删除
delete 操作
update 操作
update 修改操作如果是主键列的话,会先删除该行再插入一行目标行
如果不是主键列,会在 undo log 中直接记录原来的 update 数据行
数据库备份周期的长短我们需要仔细进行考虑