03_初步了解 InnoDB 存储引擎的架构设计
1814字约6分钟
2024-08-10
更新语句在 MySQL 中是如何执行的
MySQL
最常用的就是 InnoDB
存储引擎,我们借助一条更新语句的执行,来初步了解一下 InnoDB
存储引擎的架构设计,假设我们有下面这样一条 SQL
update user set name = 'xxx' where id = 10;
通过前面分析的 MySQL
架构整体设计,我们来想一下这条 SQL
语句是如何执行的
首先我们的系统通过一个数据库连接将 SQL
发送到 MySQL
上,然后经过 SQL
接口、解析器、优化器、执行器这几个环节,解析 SQL
语句,生成执行计划,接着由执行器去负责这个计划的执行,调用 InnoDB
存储引擎的接口去执行,大致流程如下图所示
缓冲池:InnoDB 非常重要的内存结构
缓冲池是 InnoDB
存储引擎非常重要的放在内存里的组件,会缓存很多数据,以便于以后查询的时候,内存缓冲池里有数据就不用去查磁盘了
InnoDB
存储引擎要执行更新语句的时候,会先看一下 id = 10
这条数据是否在缓冲池里,不在的话就会从磁盘里加载到缓冲池里来
因为是更新语句,肯定是不允许别人同时修改的,所以还会对这行记录加独占锁,如下图所示
undo 日志文件:让更新的数据可以回滚
我们都知道,执行一个更新语句,在事务提交之前我们都是可以对数据进行回滚的,所以为了考虑可能要回滚数据,这里会把更新前的值写入 undo
日志文件中,如下图所示
更新 buffer pool 中的缓存数据
当我们把要更新的数据从磁盘文件加载到缓冲池,同时对他加锁并且把更新前的旧值写入 undo 日志文件之后,我们就可以开始更新这行数据了
假设 id = 10
这行数据的 name
原来的值是 zhangsan
,更新缓冲池里的数据意思就是把内存里 id = 10
这行数据的 name
字段修改为 xxx
由于此时只是更新了缓冲池中的数据,磁盘上 id = 10
的数据还是 zhangsan
,所以这行数据就是脏数据了,如下图所示
Redo Log Buffer:万一系统宕机,如何避免数据丢失
按照上面的说明,现在我们已经把内存里的数据进行了修改,但是磁盘的上的数据还没修改,万一此时 MySQL
所在机器宕机了,必然会导致内存里修改过的数据丢失,这要怎么办?
这个时候我们需要把对内存所做的修改写入到 Redo Log Buffer
里去,这也是内存里的一个缓冲区,是用来存放 redo
日志的
redo 日志就是记录下来你对数据做了什么修改,在 MySQL
突然宕机的时候,用来恢复更新过的数据,如下图所示
如果还没提交事务,MySQL 宕机了怎么办
在数据库中,执行一条 SQL
语句也可以是一个独立的事务,只有提交事务之后,SQL
语句才算执行结束
如果没有提交事务的时候 MySQL
崩溃,那么内存里 Buffer Pool
中修改的数据会丢失,写入 Redo Log Buffer
的 redo
日志也会丢失,我们看下图
此时数据丢失要紧么?
其实是不要紧的,你一条更新语句没提交事务,也就代表他没执行成功,MySQL
宕机虽然导致内存里的数据都丢失了,但是磁盘上的数据还是原来的样子
所以此时如果 MySQL
宕机,不会有任何问题
提交事务的时候将 redo 日志写入磁盘中
我们要提交一个事务的时候,会根据一定的策略把 redo
日志从 Redo Log Buffer
刷入到磁盘文件
这个策略是通过 innodb_flush_log_at_trx_commit
来配置的,他有 0、1、2
这三个选项
innodb_flush_log_at_trx_commit = 0
当这个参数值为 0
时,你提交事务的时候不会把 Redo Log Buffer
里的数据刷入到磁盘文件,此时可能你都提交事务了,结果 MySQL
宕机,然后内存里的数据全部丢失
相当于你提交事务成功了,但是由于 MySQL
突然宕机,导致内存中的数据和 redo
日志都丢失了,如下图所示
innodb_flush_log_at_trx_commit = 1
当这个参数值为 1
时,你提交事务的时候必须把 redo log
从内存刷入到磁盘文件里去,只要事务提交成功,那么 redo log
就必然在磁盘里了
只要提交事务成功,redo
日志一定在磁盘文件里,肯定会有一条 redo
日志:“我对哪个数据做了一个什么修改,比如将 user
表 name
字段修改为 xxx
了”
如果事务提交成功,redo
日志写入磁盘,此时 MySQL
系统突然崩溃了,此时会如何?会丢失数据吗?
不会丢失数据,哪怕 buffer pool
中更新过的数据还没刷新到磁盘里去
虽然内存里修改成 name = xxx
的数据会丢失,但是 redo
日志已经记录了,对某某数据做了修改
所以当 MySQL
重启之后,他可以根据 redo
日志去恢复之前做过的修改,如下图所示
innodb_flush_log_at_trx_commit = 2
当这个参数值为 2
时,你提交事务的时候会把 redo
日志写入磁盘文件对应的 os cache
缓存里去,而不是直接进入磁盘文件,可能 1
秒后才会把 os cache
里的数据写入到磁盘文件里去
这种策略下,你提交事务之后,redo log
可能停留在 os cache
缓存里,没实际进入磁盘文件
此时如果 MySQL
系统崩溃了,那么 os cache
里的 redo log
就会丢失,同样会让你感觉事务提交了,结果数据丢失了,如下图所示
对于
redo
日志的三种刷盘策略,我们通常建议是设置为1
,也就是说提交事务的时候,redo
日志必须是刷入磁盘文件的,这样可以严格保证提交事务之后,数据是绝对不会丢失的