SQL 查询执行过程
大体来说,MySQL 可以分为 Server 层和存储引擎层两部分:
- Server 层:包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能。
- 连接器:跟客户端建立连接、获取权限、维持和管理连接,建议使用长连接。
- 查询缓存:(key,value) 方式存储,命中返回、未命中执行下一步,更新时缓存失效,不建议打开。
- 解析器:进行词法分析、语义分析生成解析树,预处理器会检查解析树的合法性。
- 优化器:根据代价模型,选择索引和执行顺序,生成执行计划。
- 执行器:验证权限,调存储引擎接口返回结果,并更新查询缓存。
- 存储引擎层:与数据库文件打交道,负责数据的存储和读取。
SQL 更新执行过程
与查询流程不一样的是,更新流程还涉及两个重要的日志模块:redo log
(重做日志)和 binlog
(归档日志)。
背景:MySQL 有个问题,如果每一次的更新操作都需要写进磁盘,然后磁盘也要找到对应的那条记录,然后再更新,整个过程 IO 成本、查找成本都很高。为了解决这个问题,MySQL 的设计者就用了 Write-Ahead Logging 思想(它的关键点就是先写日志,再写磁盘)来提升更新效率。
redo log
1 | -rw-r----- 1 mysql mysql 1.0G Sep 17 07:20 ib_logfile0 |
说明:记录数据页做了什么改动,而不是这个页更新之后的状态。
作用:有了 redo log
,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe
(崩溃恢复)。
原理:InnoDB 的 redo log
是固定大小的,每个文件的大小是 1GB,从头开始写,写到末尾就又回到开头循环写。write pos
是当前记录的位置,checkpoint
是当前要擦除的位置,如果 write pos
追上 checkpoint
,表示满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint
推进一下。
参数:innodb_flush_log_at_trx_commit
这个参数设置成 1 的时候,表示每次事务的 redo log
都直接持久化到磁盘。这个参数建议设置成 1,这样可以保证 MySQL 异常重启之后数据不丢失。
binlog
1 | -rw-r----- 1 mysql mysql 1.1G Sep 11 07:17 mysql-bin.000098 |
上面介绍的 redo log
是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog
(归档日志)。
说明:binlog
有两种模式,statement
格式的话是记 sql 语句, row
格式会记录行的内容,记两条更新前和更新后都有。
作用:binlog
会记录所有的逻辑操作,并且是采用“追加写”的形式,用于备份恢复。
参数:sync_binlog
这个参数设置成 1 的时候,表示每次事务的 binlog
都持久化到磁盘。这个参数也建议设置成 1,这样可以保证 MySQL 异常重启之后 binlog
不丢失。
区别:
redo log
是 InnoDB 引擎特有的;binlog
是 MySQL 的 Server 层实现的,所有引擎都可以使用。redo log
是物理日志,记录的是“在某个数据页上做了什么修改”;binlog
是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。redo log
是循环写的,空间固定会用完;binlog
是可以追加写入的。“追加写”是指binlog
文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
update 执行流程
- 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
- 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
- 引擎将这行新数据更新到内存中,同时将这个更新操作记录到
redo log
里面,此时redo log
处于prepare
状态。然后告知执行器执行完成了,随时可以提交事务。 - 执行器生成这个操作的
binlog
,并把binlog
写入磁盘。执行器调用引擎的提交事务接口,引擎把刚刚写入的redo log
改成提交commit
状态,更新完成。
将 redo log
的写入拆成了两个步骤:prepare
和 commit
,这就是 “两阶段提交”。简单说,redo log
和 binlog
都可以用于表示事务的提交状态,而两阶段提交就是让这两个数据状态保持逻辑上的一致。