MySQL技术内幕Chapter7事务
一、事务分类
1.扁平事务
使用最为频繁的事务,主要限制在于不能提交或者回滚事务的某一部分
2.带有保存点的事务
允许事务在执行过程中回滚到同一事务中较早的一个状态,保存点使用SAVE WORK函数建立,通知系统当前处理状态。
3.链事务
带有保存点的扁平事务在系统崩溃时都将消失,其保存点是易失的,当进行恢复时,事务需要从头开始执行,而非最近的保存点。
链事务思想在于:提交事务时,释放不需要的数据对象,将必要的处理上下文隐式的传给下一个要开始的事务。提交事务操作和下一个事务操作将合并为一个原子操作。
链事务与带有保存点的事务不同之处在于回滚只能是当前事务,而非任意保存点。
4.嵌套事务
任何子事务都在顶层事务提交后才生效
任意事务的回滚都会引起所有子事务的回滚
实际工作由叶节点事务完成,而高层的事务仅负责逻辑控制
5.分布式事务
二、事务的实现
1.redo
undo与redo区别:前者负责数据库事务的一致性,后者是数据库事务的原子性与持久性。redo恢复提交事务修改的页操作,而undo回滚行记录到某个特定的版本。redo是物理日志,记录页的物理修改操作,undo 是逻辑日志,根据每行进行记录。
redo用于实现事务的持久性,有两个部分组成,一是内存中重做日志缓冲,二是重做日志文件。
innodb通过Force Log at Commit机制实现事务的持久性,即当事务提交时,必须将事务的所有日志写入到重做日志文件持久化。innodb中重做日志包括redo 与undo,redo是顺序写,在数据库运行时无需对redo进行读操作,而undo是需要随机读写的。
为确保所有日志文件都会写入日志文件,在将重做日志缓存写入重做日志文件中后,innodb需要调用一次fsync操作。日志缓存会先写入到文件缓存中,再进行一次fsync操作,而fsync效率取决于磁盘的性能。
当然重做日志在事务提交时也可不写入日志文件,而是等待一定时间周期后再执行fsync,由于非强制故会导致宕机时数据丢失。
参数innodb_flush_log_at_trx_commit控制重做日志刷新到磁盘的策略,默认为1;为0则提交不写入重做日志文件,仅在master Thread中有;为2则事务提交时写入重做日志文件但是仅写入文件系统缓存,不进行fsync操作。
binlog与redolog
binlog 主要用于数据库POINT-IN-TIME的恢复以及主从复制
redo 主要是存储引擎层产生,而binlog是数据库层产生,任何存储引擎对于数据库的改动都会产生,记录的是逻辑日志,是对应的SQL语句,且只在数据库事务提交后进行写入。而redo则是物理日志,是对页的操作记录,在事务进行过程中不断写入。
binlog与每个事务一一对应,而redo是一个事务对应多个日志,且事务的redo写入时并发的,故其在文件中记录的顺序并非事务开始的顺序。
2.log block
重做日志都是以块的形式保存,大小512B
3.格式
LSN:log sequence number,代表日志的***,innodb中LSN占用8B,表示含义有:
1.redo 写入总量 2.Checkpoint位置 3.页的版本
LSN表示事务写入redo 的字节总量
4.恢复
checkpoint表示已经刷新到磁盘上的LSN,因此在恢复过程中仅需恢复Checkpoint开始的日志部分,如上图,仅需恢复10000-13000之间部分日志。
三.undo
1.用于数据库回滚,存放在数据库内部的特殊段中,称为undo段,位于共享表空间。
undo是逻辑日志,只是将数据库逻辑上恢复到原来的状态,所有修改逻辑上被取消,但是数据结构与页本身在回滚前后可能不同。
另一个作用是MVCC
2.undo存储管理
rollback segment记录1024个undo log segment,在每个undo log segment中进行undo页的申请
事务在undo log segment分配页并写入undo log时同样需要写入重做日志。事务提交时,innodb将:1.将undo log放入列表中,以供后续purge 2.判断undo log所在页是否可重用。
事务提交后并不会立即删除undo log与其所在页,以防其他事务需要使用。事务提交时将undo log放入链表中,是否可以删除最终由purge线程来判断。
判断undo页使用空间是否小于3/4,若是则表示该undo页可以被重用,之后新的undo记录会加在其后。
分为insert undo log与update undo log
delete操作并不会直接删除记录,而是将记录标记为已删除(delete flag设置为1),而最终的删除由purge操作完成。
3.purge
用于完成delete与update操作,这样设计是因为MVCC
innodb中的history list根据事务提交顺序将undo log进行链接,先提交的事务在末尾。
一个undo page可以存放多个不同事务的undo log。
执行purge时,从history list中找第一个需要被清理的记录,为tx1,接着innodb会在undo log所在页中继续寻找是否可以继续被清理的记录,为tx3与之后的tx5,但是tx5被事务占用,不能清理,故再次到history list中寻找,此时为tx2,同理清理完tx2,tx6,tx4,此时undo page2中所有页都被清理,可以被重用。
先从history list中找undo log 再从undo page中找undolog是为了避免大量随机读取操作,提高purge效率
可通过innodb_purge_batch_size设置每次purge的undo page数量
4.group commit
对于非只读事务,每次提交时都需进行fsync操作,但是易受限于磁盘的性能,当前数据库采用group commit,即一次fsync,多个事务日志写入文件,以提高性能。
MySQL5.6采用BLGC以解决之前开启二进制日志后,group commit失效的问题。
原理如下:
事务提交时,首先按照顺序将其放入队列中,队列中第一个事务称为leader,其他称为follower,leader控制follower的行为,BLGC分为下述三个步骤
1.flush:将每个事务的二进制日志写入到内存中;
2.sync:将内存中二进制日志刷新到磁盘,若队列中有多个事务,则依次fsync完成二进制日志的写入;
3.commit:leader根据顺序调用存储引擎层的事务的提交