第14章 MySQL事务日志
事务有4种特性:原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢?
- 事务的隔离性由
锁机制
实现。 - 而事务的原子性、一致性和持久性由事务的
redo
日志和undo
日志来保证。- REDO LOG 称为
重做日志
,提供再写入操作,恢复提交事务修改的页操作,用来保证事务的持久性。 - UNDO LOG 称为
回滚日志
,回滚行记录到某个特定版本,用来保证事务的原子性、一致性。
UNDO 不是 REDO 的逆过程。REDO 和 UNDO都可以视为是一种恢复操作
。
内存中的修改会先写入redo日志记录,再进行修改,如果数据库中写入磁盘之前宕机了,数据库服务器重启后,会从redo日志中进行数据的恢复。
- REDO LOG 称为
- redo log: 是存储引擎层 (innodb) 生成的日志,记录的是"物理级别"(物理磁盘上的记录)上的页修改操作,比如页号xxx,偏移量yyy写入了’zzz’数据。主要为了保证数据的可靠性。
-
- undo log: 是存储引擎层 (innodb) 生成的日志,记录的是 逻辑操作 日志,比如对某一行数据进行了
INSERT
语句操作,那么undo log就记录一条与之相反的DELETE
操作。主要用于事务的回滚
(就执行undo日志中记录的所有逆操作,undo log 记录的是每个修改操作的逆操作
) 和一致性非锁定读
(undo log 回滚行记录到某种特定的版本——MVCC,即多版本并发控制)。
- undo log: 是存储引擎层 (innodb) 生成的日志,记录的是 逻辑操作 日志,比如对某一行数据进行了
1. redo日志
InnoDB存储引擎是以页为单位
来管理存储空间的。在真正访问页面之前,需要把在磁盘上
的页缓存到内存中的Buffer Pool
之后才可以访问。所有的变更都必须先更新缓冲池
中的数据,然后缓冲池中的脏页
会以一定的频率被刷入磁盘 (checkPoint
机制),通过缓冲池来优化CPU和磁盘之间的鸿沟,这样就可以保证整体的性能不会下降太快。
1.1 为什么需要REDO日志
checkpoint机制可以保证数据写入磁盘,然而由于checkout机制是以一定的频率触发的
,不是每次变更都会触发
,而是master线程隔一段时间去处理。有可能事务提交后,刚写入缓冲池,数据库宕机,这段数据就丢失无法恢复了(不能保证持久性)。
另一方面,事务包含 持久性
的特性,就是说对于一个已经提交的事务,在事务提交后即使系统发生了崩溃,这个事务对数据库中所做的更改也不能丢失。
为了确保持久性
,将内存中执行的操作保存到物理磁盘上的一个文件中,这个日志文件就叫做redo日志。数据库如果发生了宕机,在内存中的修改由于已经记录到redo日志,此时只需要执行redo日志中的操作进行恢复操作写入磁盘(写入了之前提交的事务),保证了持久性
。
InnoDB引擎的事务采用了WAL技术 (Write-Ahead Logging
),这种技术的思想就是先写日志,再写磁盘
,只有日志写入成功,才算事务提交成功,这里的日志就是redo log
。当发生宕机且数据未刷到磁盘的时候,可以通过redo log
来恢复。
1.2 REDO日志的好处、特点
1. 好处
- redo日志降低了刷盘频率
- redo日志占用的空间非常小
存储表空间ID、页号、偏移量以及需要更新的值,所需的存储空间是很小的,刷盘快。
2 Redo日志的特点
- redo日志是顺序写入磁盘的
- 事务执行的过程中,每执行一条语句,就可能产生若干条redo日志,按顺序写入磁盘,即顺序IO,效率较高。
- 事务执行过程中,redo log不断记录
- redo log和bin log的区别,redo log是
存储引擎层
产生,bin log是数据库层
产生;不断向redo log顺序记录日志,而bin log只有在事务提交时,才一次性写入bin log文件中。
- redo log和bin log的区别,redo log是
2. undo日志
redo log是事务持久性的保证,undo log是事务原子性的保证。在事务中 更新数据
的 前置操作
其实是要先写入一个 undo log
。
2.1 如何理解Undo日志
事务需要保证 原子性
,也就是事务中的操作要么全部完成,要么什么也不做。但有时候事务执行到一半会出现一些情况,比如:
- 情况一:事务执行过程中可能遇到各种错误,比如
服务器本身的错误
,操作系统错误
,甚至是突然断电
导致的错误。 - 情况二:程序员可以在事务执行过程中手动输入
ROLLBACK
语句结束当前事务的执行。
以上情况出现,我们需要把数据改回原先的样子,这个过程称之为回滚
,这样就可以造成一个假象:这个事务看起来什么都没做,所以符合原子性
要求。
MySQL将为了回滚而记录这些内容称之为撤销日志
或回滚日志
(即undo log
)。查询操作不会修改任何用户记录,不需要记录查询操作的undo日志。
此外,undo log 会产生 redo log,也就是说undo log需要持久性的保护。
2.2 undo日志的作用
- 作用1:回滚数据
- undo日志是
逻辑日志
,将所有的更改逻辑上的撤销,使得数据库恢复到以前的样子,但是数据结构和页本身与回滚之后可能有很大不同。
- undo日志是
- 作用2: MVCC
- undo log的另一个作用是MVCC,即在InnoDB存储引擎中MVCC的实现是需要undo log参与的。当用户读取一行记录时,如果该记录被其他事务占用,当前事务可以通过undo log获取之前的行版本信息,以实现非锁定读取。
2.3 undo的存储结构(概要)
undo页的重用和undo log链
当我们开启一个事务需要写undo log的时候,就先去undo log segment中找到一个空闲的位置,当有空位时,就申请undo页,再到这个申请到的undo页中进行undo log的写入,一个页的默认大小是16K,会造成大量资源浪费。
于是undo页就被设计为可重用,数据提交时,不会立即删除undo页,undo log在commit之后,会被放入一个链表
中,然后判断undo页的使用空间是否<3/4
,如果如此,当前的undo页就可以被重用,其他的undo log记录在当前undo页的后面,因此undo log是离散的
。
2.4 undo的类型
在InnoDB存储引擎中,undo log分为:
- insert undo log
insert undo log是指insert操作中产生的undo log。因为insert操作的记录,只对事务本身可见,对其他事务不可见(这是事务隔离性的要求),故该undo log可以在事务提交后直接删除。不需要进行purge操作。 - update undo log
update undo log记录的是对delete和update操作产生的undo log
。该undo log
可能需要提供MVCC机制
,因此不能在事务提交时就进行删除。提交时放入undo log链表
,等待purge线程进行最后的删除。
2. 详细生成过程
对于InnoDB引擎来说,每个行记录除了记录本身的数据之外,还有几个因此的列:
row_id
:如果没有为表显式地定义主键,并且表中也没有定义唯一索引,那么InnoDB会自动为表添加一个row_id
的隐藏的列作为主键。trx_id
:每个事务会被分配一个事务id,当这个事务对某条记录变更之后,就会把这个事务的事务id写入这条记录的trx_id
中。roll_ptr
:回滚指针,本质上就是指向undo log的指针。
当我们执行INSERT时:
插入的数据都会生成一条insert undo log,并且数据的回滚指针会指向它。undo log会记录undo log的序号、插入主键的列和值…,那么在进行rollback的时候,通过主键直接把对应的数据删除即可。
当我们执行UPDATE时:
对应更新的操作会产生update undo log,并且会分更新主键和不更新主键的,假设现在执行:
UPDATE user SET name="Sun" WHERE id=1;
这时会把老的记录写入新的undo log,让回滚指针指向新的undo log,它的undo no是1,并且新的undo log会指向老的undo log(undo no=0)。
再执行
UPDATE user SET id=2 WHERE id=1;
对于更新主键的操作,会先把原来的数据deletemark标识打开,这时并没有真正的删除数据,真正的删除会交给清理线程去判断,然后在后面插入一条新的数据,新的数据也会产生undo log,并且undo log的序号会递增。
**可以发现每次对数据的变更都会产生一个undo log,当一条记录被变更多次时,那么就会产生多条undo log,undo log记录的是变更前的日志,**并且每个undo log的序号是递增的,那么当要回滚的时候,按照序号依次向前推
,就可以找到我们的原始数据了。
4. undo log的删除
- 针对于insert undo log
因为insert操作的记录,只对事务本身可见,对其他事务不可见。故该undo log可以在事务提交后直接删除,不需要进行purge操作。 - 针对于update undo log
该undo log可能需要提供MVCC机制
,因此不能在事务提交时就进行删除。提交时放入undo log链表
,等待purge线程进行最后的删除。purge线程两个主要作用是:
清理undo页
和清理page里面带有Delete_Bit标识的数据行
。在InnoDB中,事务中的Delete操作实际上并不是真正的删除掉数据行,而是一种Delete Mark操作,在记录上标识Delete_Bit,而不删除记录。是一种“假删除”,只是做了个标记(逻辑删除),真正的删除工作需要后台purge线程去完成。
2.6 小结
undo log是逻辑日志,对事务回滚时,只是将数据库逻辑地恢复到原来的样子。
redo log是物理日志,记录的是数据页的物理变化,undo log不是redo log的逆过程。