「本文已参与 周末学习计划,点击查看详情」
这是我参与更文挑战的第6天,活动详情查看:更文挑战
一、前言
场景:存储引擎完成一条更新语句的执行。
InnoDB
的重要内存结构:缓冲池(Buffer Pool
)
这里面会缓存很多的数据,以便于以后在查询的时候,万一要是内存缓冲池里有数据,就可以不用去查磁盘了。
如图:
二、举个栗子:
执行更新 SQL
,会发生什么呢?
UPDATE user SET name = 'donaldy' WHERE id=10;
复制代码
步骤:
- 缓冲池中数据操作
undo
日志文件:保存旧值redo log buffer
:保存新值- 后台线程不定时刷新内存里的数据到磁盘文件里。
为什么不直接更新磁盘上的数据?
因为执行性能极差。
来一个请求就直接对磁盘文件进行随机读写,然后更新磁盘文件里的数据,虽然技术上可以实现,但导致执行请求的性能极差。
所以 MySQL
才设计了这样复杂的一套机制,通过内存里更新数据,然后写 redo log
以及事务提交,后台线程不定时刷新内存的数据到磁盘文件中。
1)缓冲池中数据操作
概括:将数据从磁盘读取,放到内存中操作,操作完成后,再写入磁盘
详细步骤如下:
- 查看
id=10
这一行数据是否在缓冲池里 - 若缓冲池有,则会对这行记录加独占锁
- 若不在的话,会直接从磁盘里加载到缓冲池里来
- 操作完成后,写入磁盘
问题:为什么用独占锁?
保证原子操作
2)undo
日志文件:保存旧值
如图:
旧值:更新之前的值
# 原先数据:id = 10, name = 'gege'
# 执行SQL
UPDATE user SET name = 'donaldy' WHERE id=10;
# 那么 'gege' 就是旧值
复制代码
为什么要保存旧值?
为了方便之后回滚,旧值恢复。
3)redo log buffer
:保存新值
万一系统宕机了,内存中数据丢失了,怎么办?
这时候要把内存中所做的修改写入到一个
Redo Log Buffer
里去,这是内存里的一个缓冲区,用来存放redo
日志的。
功能:其实是用来在 MySQL
突然宕机的时候,用来恢复更新过的数据。
问题:如果还没提交事务,MySQL
宕机了怎么办?
此问题分三种情况:
- 事务提交之前
- 事务提交之中
- 事务提交成功
- 事务提交之前:
还没有提交事务,此时
MySQL
崩溃了,必然导致内存里Buffer Pool
中的修改过的数据都丢失,同时写入Redo Log Buffer
中的redo
日志也会丢失。
没提交事务,就代表没执行成功,此时MySQL
宕机虽然导致内存里的数据都丢失了,但磁盘上的数据依然还停留在原样子。
所以,此时如果MySQL
宕机,不会有任何的问题。
- 事务提交之中:
前置:提交一个事务,此时会根据一定的策略把 redo
日志从 redo log buffer
里刷入到磁盘文件里。
这个策略通过
innodb_flush_log_at_trx_commit
来配置的。
- 参数值为 0 时,不会把
redo log buffer
里的数据刷入磁盘文件,此时提交事务,MySQL
宕机,内存里的数据全部丢失。- 参数值为 1 时,就必须把
redo log
从内存刷入磁盘文件里去,只要事务提交成功,那么redo log
必然在磁盘里。- 参数值为 2 时,提交事务的时候,把
redo
日志写入磁盘文件对应的os cache
内存缓存里,没实际进入磁盘文件,万一此时要是机器宕机了,那么os cache
里的redo log
就会丢失; 即提交事务成功,而数据丢失了。
如图:
所以根据参数值,不同会有不同结果。
- 事务提交成功:
如上,会根据 innodb_flush_log_at_trx_commit
参数值,而会有不同结果