一条 SQL 更新语句是如何执行的?

之前你可能经常听 DBA 同事说,MySQL 可以恢复到半个月内任意一秒的状态,惊叹的同时,你是不是心中也会不免会好奇,这是怎样做到的呢?

我们先从一条更新语句讲起,首先创建一个表,这个表有一个主键ID,和一个整形的value

sql 复制代码
mysql> create table T(ID int primary key, c int);

更新ID=2的记录,将它的value+1

sql 复制代码
mysql> update T set c=c+1 where ID=2;

更新语句和前面的查询语句一样,会经过连接器、分析器、优化器、执行器等一系列操作。

但是更新语句和查询也有不同的地方,那就是日志模块redo log(重做日志)和 binlog(归档日志)

2.1redo log

MySQL每一次的更新操作都需要写入进磁盘,在磁盘中找到对应的记录进行更新,整个IO操作的成本很高,为了解决这个问题就出现了redo log这种临时记事本的形式来提升效率。

而这个临时记事本的过程就是MySQL里经常说到的WAL技术,WAL 的全称是 Write-Ahead Logging,它的关键点就是先写日志,再写磁盘,也就是先写粉板,等不忙的时候再写账本。

具体来说,当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写到 redo log(粉板)里面,并更新内存,这个时候更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候做,这就像打烊以后掌柜做的事

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

write pos就是当前记录更新的位置,它不断的往前写,check point是当前要擦除的位置,它也会不断的向前。write pos碰到check point后就会停下来,这时候就要先擦一些记录,让check point往前移动,腾出一些空间来记录redo log

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

2.2binlog

MySQL整体就2块,一块是Server层,一块是存储引擎层,redo log 是innoDB存储引擎的日志,binlog是Server层的日志。

为什么有2份日志呢?

因为最开始MySQL是没有innoDB引擎的,MyISAM才是MySQL自带的引擎,但是MyISAM没有crash-safe的能力。这时候出现了innoDB,它是其它公司开发的,以插件的形式引入到MySQL中,因为binlog只能用于归档,所以它们开发了一套新的日志系统------ redo log

这两种日志有以下三点不同。

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

有了对这两个日志的概念性理解,我们再来看执行器和 InnoDB 引擎在执行这个简单的 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)状态,更新完成。

为什么必须有"两阶段提交"呢?这是为了让两份日志之间的逻辑一致。

相关推荐
ccddsdsdfsdf7 小时前
DBeaver怎么链接mongoDB
数据库·mongodb
丷丩8 小时前
Postgresql基础实践教程(十一)各种Join
数据库·postgresql·join
星夜夏空998 小时前
FreeRTOS学习(4)——内存映射
数据库·学习·mongodb
TheRouter8 小时前
AI Agent 记忆体系建设实战:短期、长期与工作记忆的工程实现
数据库·人工智能·oracle
Omics Pro9 小时前
首个!外源天然产物综合性代谢图谱
数据库·人工智能·算法·机器学习·r语言
唐青枫9 小时前
MySQL EXISTS 详解:存在性判断、NOT EXISTS 与实战示例
sql·mysql
JAVA面经实录91710 小时前
Hibernate面试题库
数据库·oracle·hibernate
迷枫71210 小时前
DM8 目录结构与常用排查入口梳理
服务器·数据库
Mr.Daozhi11 小时前
RAG 进阶实战:跑通 Demo 后我连续翻了 6 次车,逐一修复才真正可用(含 Gradio Web 版)
前端·数据库·langchain·大模型·gradio·rag·科研工具
小程故事多_8011 小时前
Claude Code自定义workflow skills用法
数据库·人工智能·智能体