这是我参与更文挑战的第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 参数值,而会有不同结果