MySQL事务一致性的实现

如何保障原子性

为了保证事务的原子性(要不然成功,要不然就没做)

在有好多操作组成一个事务的时候,我们就需要undo 日志

假设有A、B两个数据,值分别为1,2。
A.事务开始.
B.记录A=1到undo log.
C.修改A=3.
D.记录B=2到undo log.
E.修改B=4.
F.将undo log写到磁盘。
G.将数据写到磁盘。
H.事务提交

如果挂了怎么办?回滚到a=1,b=2,这就要通过undo log了

Redo log的使用可以提升磁盘IO,

假设有A、B两个数据,值分别为1,2.
A.事务开始.
B.记录A=1到undo log.
C.修改A=3.
D.记录A=3到redo log.
E.记录B=2到undo log.
F.修改B=4.
G.记录B=4到redo log.
H.将redo log写入磁盘。
I.事务提交

数据写在缓存,但是Redo log已经刷入磁盘了。

redo log

在InnoDB存储引擎中,事务日志通过重做日志文件(redo log)和日志缓冲(InnoDB log buffer)实现。当一个事务开始时,会记录事务的一个LSN(Log Sequence Number);当事务执行时,会往InnoDB的日志缓冲中插入事务日志;当事务提交时,必须先将日志缓冲写入磁盘,换言之,就是先写日志在写数据,这种方式称作预写日志(WAL)。
InnoDB的redo log都是通过mtr产生的,先写到mtr的cache中,然后再提交到公共buffer中。

redo 组提交

将多个事务redo log合并一起写到Redo Log的全局buffer里,通过LSN标记Relo Log的大小和在buffer中的边界,从而写盘刷盘动作合并,减少磁盘顺序写。

RedoLog 对应的buffer空间是全局的,每个线程是可以直接访问(访问之前当然要获得锁,以免访问冲突)。一个或多个session在执行各自的事物(即SQL语句)时,生成一条或多条RedoLog后随时就把此log写入RedoLog的全局buffer空间,多个session对应的RedoLog是依次交替的记录在一起的。程序中有个全局变量记录着最后一条log对应的LSN,当满足某些条件时(buffer空间不足时,事物提交时,或者RedoLog flush线程一秒会flush一次)程序中某一个线程就会把RedoLog buffer 中的所有的log 写入到RedoLog文件。

undo log

重做日志记录了事务的行为,可以方便的通过其进行“重做”。但事务有时还需要撤销,这是就需要undo。undo与redo正好相反,undo是先写数据后记录undo log;事务对数据库进行修改时,会产生redo和undo,当有错误或rollback时,可以根据undo log将数据恢复到修改前的样子;undo存放在数据库内部的特殊段中(segment),undo段位于共享表空间内。
undo并非对数据的物理恢复,而是逻辑恢复,例如插入了10万条数据,使表空间增加,undo后表空间并不会收缩;对于每条insert,undo是做相应的delete,对于update则是做了相反的update。

undo是逻辑日志,因此只是将数据库逻辑地恢复到原来的样子。所有修改都被逻辑地取消了,但是数据结构和页本身在回滚之后可能大不相同。

事务回滚

如果事务因为异常或者被显式的回滚了,那么所有数据变更都要改回去。这里就要借助回滚日志中的数据来进行恢复了。
操作也比较简单,析取老版本记录,做逆向操作即可:对于标记删除的记录清理标记删除标记;对于in-place更新,将数据回滚到最老版本;对于插入操作,直接删除聚集索引和二级索引记录(row_undo_ins)。

多版本控制

InnoDB的多版本使用undo来构建,这很好理解,undo记录中包含了记录更改前的镜像,如果更改数据的事务未提交,对于隔离级别大于等于read commit的事务而言,它不应该看到已修改的数据,而是应该给它返回老版本的数据。
undo log会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护

undo 的重用

当事务提交时,首先将undo log放入链表中,然后判断undo页的使用空间是否小于3/4,若是则表示该undo页可以被重用,之后新的undo log记录在当前undo log的后面。由于存放undo log的列表是以记录进行组织的,而undo页可能存放着不同事务的undo log,因此purge操作需要涉及磁盘的离散读取操作,是一个比较缓慢的过程。

参考

您的支持将鼓励我继续创作 笔芯