1. 前言
一条简简单单的更新操作涉及到太多的知识点了,首先我们要1.了解MySQL各个功能模块,然后在MySQL更新时不仅仅是操作表数据还要操作2.日志系统redo log、binlog和undo log,更新操作也不是实时更新到磁盘的而是通过3.Write-Ahead Logging机制先刷到内存再刷到磁盘,要刷到内存写日志的顺序又涉及到4.二阶段提交。
2. 更新流程
首先是MySQL的各个功能模块,在MySQL查询过程会详细介绍。
数据库连接,然后通过分析器进行词法分析和语法分析,再经过优化器选择索引等。
执行器先找引擎取ID=2这一行。 ID是主键, 引擎直接用树搜索找到这一行。 如果ID=2这一
行所在的数据页本来就在内存中, 就直接返回给执行器; 否则, 需要先从磁盘读入内存, 然
后再返回。
执行器拿到引擎给的行数据, 把这个值加上1, 比如原来是N, 现在就是N+1, 得到新的一行
数据, 再调用引擎接口写入这行新数据。
引擎将这行新数据更新到内存中, 同时将这个更新操作记录到redo log里面, 此时redo log处
于prepare状态。 然后告知执行器执行完成了, 随时可以提交事务。
执行器生成这个操作的binlog, 并把binlog写入磁盘。
执行器调用引擎的提交事务接口, 引擎把刚刚写入的redo log改成提交(commit) 状态, 更
新完成。
3. MySQL日志系统
在MySQL中,有三种日志。分别是redo log、binlog和undo log。
3.1 redo log 和 binlog
1、存储的内容
可以这样理解,binlog记载的是update/delete/insert这样的SQL语句,而redo log记载的是物理修改的内容(xxxx页修改了xxx)。所以在搜索资料的时候也会有这样的说法:binlog 记录的是数据的逻辑变化,redo log 记录的是数据的物理变化
2、功能
redo log的作用是实现持久化。数据库更新写完内存,如果数据库挂了,那我们可以通过redo log来恢复内存还没来得及刷到磁盘的数据,将redo log加载到内存里边,那内存就能恢复到挂掉之前的数据了。
binlog的作用是进行复制和恢复。
主从服务器需要保持数据的一致性,通过binlog来同步数据。
如果整个数据库的数据都被删除了,binlog存储着所有的数据变更情况,那么可以通过binlog来对数据进行恢复。
3、载体
redo log是InnoDB引擎特有的。binlog是MySQL的Server层实现的,所有引擎都可以使用。
4、记录方式
redo log是循环写的,空间固定会用完;binlog是追加写入的,一个文件写满后会切换到下一个文件而不会去覆盖以前的日志。
3.2 undo log
undo log主要有两个作用:回滚和多版本并发控制(MVCC)
在数据修改的时候,不仅记录了redo log,还记录undo log,如果因为某些原因导致事务失败或回滚了,可以用undo log进行回滚undo log主要存储的也是逻辑日志,比如我们要insert一条数据了,那undo log会记录的一条对应的delete日志。我们要update一条记录时,它会记录一条对应相反的update记录。
回滚的实现就是找到undo log中对应的相反操作语句执行。
而多版本并发控制则是利用undo log做版本的回退(聊MVCCC时再具体讲)
4. Write-Ahead Logging机制
使用日志,存储引擎可以在内存中更新数据,然后将更新持久化到磁盘的日志文件中,不需要每次都将更新后的数据刷新到磁盘(随机IO)
日志采用的是追加方式,写日志的操作实在磁盘上一小块区域的顺序IO(比随机IO快得多)
日志持久化到磁盘后,内存中被修改的数据(脏页)在后台可以慢慢刷新到磁盘
5. 二阶段提交
上图执行过程中,redo log分为prepare阶段和commit阶段,在写入binlog的前后执行,这就是二阶段提交。
当commit 命令执行时,
写入redo log进入prepare阶段:事务进入commit prepare 阶段,事务中新生成的redo log 会被刷到磁盘
写入bin log:把binlog日志刷到磁盘
redo log处于commit状态更新完成:innodb释放锁,清除undo信息,设置redo log提交状态。
5.1 为什么需要二阶段提交
由于存在redo log 和 binlog ,而他们两是相互独立的。而事务提交必须确保两者同时有效。不然会出现不一致的情形。我们对redo log和binlog不进行二阶段提交的顺序进行假设。
**先写redo log再写binlog:**redo log写了,binlog还没写,数据库崩了。通过redo log恢复数据库能将这条事务执行,但是binlog没有记录,从数据库就不能执行这条事务(或者在对数据库回到某个点时会没有这条事务),造成不一致的情况。
**先写binlog再写redo log:**binlog写了,redo log还没写,数据库崩了。通过redo log恢复数据库没有这条事务,但是binlog记录了,从数据库会执行这条事务(或者在对数据库回到某个点时会有这条事务的执行),造成不一致的情况。
5.2 二阶段提交怎么解决问题
上图的①时出现问题怎么解决?
这个时候redo log已经到磁盘了。binlog没有刷到磁盘所以会消失。服务器从故障中恢复时,读取磁盘中的redo log ,但是由于对应的redo log项还是prepare状态,就要判断binlog 是否完整,如果binlog完整则提交事务,如果binlog不完整则回滚事务。
上图的②时出现问题怎么解决?
这个时候redo log 和 binlog都已经存磁盘,服务器从redo log恢复就好了。
怎么判断binlog的完整性?
statement 模式的 binlog,最后会有 COMMIT;
row 模式的 binlog,最后会有一个 XID event
注:binlog有三种模式:statement、row和mixed(statement和row的混合)
找到redo log后怎么找到binlog?
redo log和binlog有一个共同的数据字段,叫 XID。崩溃恢复的时候,会按顺序扫描 redo log:如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。
参考文章:
浅谈mysql的两阶段提交协议
MySQL实战45讲——丁奇