事务执行过程中遇到意外或者手动通过rollback
语句执行 回滚 操作,需要记录 撤销日志 ,即:undo日志,只有在一个事务第一次执行 增、删、改 操作时才会为该事务分配一个 事务id,在MySQL5.5之前undo日志是记录在系统表空间(MySQL数据目录下的ibdata1就是系统表空间),长事务占用大量存储空间
事务id生成:
服务器内存维护全局变量,分配事务id时,将该变量分配给事务并将变量增1
当变量的值是256的倍数时,将该变量刷新到 系统表空间 5号页面中
Max Trx ID
位置处,该变量占8字节重启时,将
Max Trx ID
属性加载到内存,该值加256赋值给全局变量(因上次关机,全局变量可能大于Max Trx ID
,也就是说在上次关机之前[Max Trx ID
,Max Trx ID+256
]之间的事务id以今分配给一些事务,如果重启后再在此区间内赋值可能会产生冲突)
undo日志记录的到FIL_PAGE_UNDO_LOG
类型页面,该页面可以从 系统表空间 分配,也可以从 undo日志 表空间分配。
15.1 undo日志写入过程
没有被重用的undo页面,第一个页面写入undo日志前填充Undo Page Header
、Undo Log Segment Header
、Undo Log Header
这三个部分,其他页面只填充Undo Page Header
当链表只有一个undo页面并且已经使用空间小于整个页面的3/4,则该页面可被重用,其中insert undo
链表的页面可以直接覆盖之前的undo日志,而update undo
15.2 回滚段
回滚段只包含一个 Rollback Segment Header 类型的页面,该页面存放当前事务用到的所有undo双向链表的基节点所在页号,这些页号称为 undo solt
回滚段页面各个属性:
-
TRX_RSEG_MAX_SIZE:该事务回滚段管理所有undo页面数量总和最大值(默认值0xFFFFFFFF)
-
TRX_RSEG_HISTORY_SIZE:
Historty链表
占用页面数量 -
TRX_RSEG_HISTORY:
Historty链表
基节点 -
TRX_RSEG_FSEG_HEADER:回滚段的
Segment Header
结构(即:INODE Entry 结构所处表空间ID、所在页面页号、所在页面内的偏移量) -
TRX_RSEG_UNDO_SLOTS:各双向链表的页号集合,即:
undo solt
集合(最大存储1024个)
一个回滚段页面(Rollback Segment Header
类型页面)存储1024个undo solt
(undo双向链表头节点),当undo solt
被占满时会报错Too many active concurrent transactions
(可能重新执行时有别的事务提交了,该事务就可以被分配Undo页面
链表了) 当一个事务被提交,如果它的undo slot
能被重用则该链表的 TRX_UNDO_STATE 属性被设置为 TRX_UNDO_CACHED,被重用的页面加入到一个双向链表等待被重用(分为insert、update两种重用链表),如果undo slot
指向页面不能被重用,如果是insert undo
则将 TRX_UNDO_STATE 属性设置为 TRX_UNDO_TO_FREE 释放页面并且将对应的undo slot
设置为 FIL_NULL;如果是update undo
则 TRX_UNDO_STATE 属性被设置为 TRX_UNDO_TO_PRUGE 并且将对应的undo slot
设置为 FIL_NULL,然后将对应的undo写入到 History链表
MySQL中有128个回滚段,每个回滚段只有一个 Rollback Segment Header 类型页面,每个回顾i页面有1024个 undo slot,也就是可以保存1024个
undo链表
(即如果每个事务只分配一个undo页面链表,最大可以同时执行1024*128个事务),这128个 Rollback Segment Header 类型页面存储在系统表空间5号页面 0号、33~127号回滚段记录普通表的undo链表(0号回滚段必须在系统表空间,33~127可以在系统表空间也可以在用户定义undo表空间),1~32号回滚段记录临时表的undo链表,必须在临时表空间 通过启动参数innodb_undo_directory
指定undo表空间
所在目录如果没有配置则默认undo表空间就是系统表空间,通过innodb_undo_tablespaces
指定undo表空间
数量,如果指定了undo表空间
则0号回滚段处于不可用状态,undo表空间
当文件增大到一定程度会自动截断成一个小文件,而系统表空间只能不断增长
15.7 分配undo页面过程
-
首先到系统表空间5号页面获取一个
Rollback Segment Header
页面的地址(获取回滚段) -
如果回滚段两个
cached链表
有可重用的undo slot
则分配给事务 -
如果没有可重用的
undo slot
则找一个可用的空闲undo slot
(即:undo slot
值为 FIL_NULL) -
如果
undo slot
从cached链表
获取则不用分配Undo Log Segment
,否则需要重新分配一个Undo Log Segment
-
并发执行不同的事务也可以在相同的回滚段,只要
undo slot
不同即可