- GreatSQL 社区原创内容未经受权不得随便应用,转载请分割小编并注明起源。
- GreatSQL 是 MySQL 的国产分支版本,应用上与 MySQL 统一。
文章导读:
什么是 Undo Log?
Undo:
意为撤销或勾销,以撤销操作为目标,返回某个状态的操作。
Undo Log:
数据库事务开始之前,会将要批改的记录放到 Undo 日志里,当事务回滚时或者数据库解体时,能够利用 UndoLog 撤销未提交事务对数据库产生的影响。
Undo Log 是事务原子性的保障。在事务中更新数据的前置操作其实是要先写入一个 Undo Log
如何了解 Undo Log
事务须要保障 原子性,也就是事务中的操作要么全副实现,要么什么也不做。但有时候事务执行到一半会呈现一些状况,比方:
- 状况一:事务执行过程中可能遇到各种谬误,比方服务器自身的谬误,操作系统谬误,甚至是忽然断电导致的谬误。
- 状况二:DBA 能够在事务执行过程中手动输出 ROLLBACK 语句完结以后事务的执行。
以上状况呈现,咱们须要把数据改回原先的样子,这个过程称之为回滚。
每当咱们要对一条记录做改变时(这里的改变能够指INSERT、DELETE、UPDATE
),都须要 ” 留一手 ”——把回滚时所需的货色记下来。比方:
- 你插入一条记录时,至多要把这条记录的主键值记下来,之后回滚的时候只须要把这个主键值对应的记录删掉就好了。(对于每个
INSERT
, InnoDB 存储引擎会实现一个DELETE
) - 你删除了一条记录,至多要把这条记录中的内容都记下来,这样之后回滚时再把由这些内容组成的记录插入到表中就好了。(对于每个
DELETE
,InnoDB 存储引擎会执行一个INSERT
) - 你批改了一条记录,至多要把批改这条记录前的旧值都记录下来,这样之后回滚时再把这条记录更新为旧值就好了。(对于每个
UPDATE
,InnoDB 存储引擎会执行一个相同的 UPDATE
,将批改前的行放回去)
MySQL 把这些为了回滚而记录的这些内容称之为 撤销日志
或者 回滚日志
(即 Undo Log)。留神,因为查问操作(SELECT)并不会批改任何用户记录,所以在杳询操作行时,并不需要记录相应的 Undo 日志
此外,Undo Log 会产生Redo Log
,也就是 Undo Log 的产生会随同着 Redo Log 的产生,这是因为 Undo Log 也须要持久性的爱护。
Undo Log 的性能
- 提供数据回滚 - 原子性
当事务回滚时或者数据库解体时,能够利用 Undo Log 来进行数据回滚。
- 多个行版本控制(MVCC)- 隔离性
即在 InnoDB 存储引擎中 MVCC 的实现是通过 Undo 来实现。当用户读取一行记录时,若该记录曾经被其余事务占用,以后事务能够通过 Undo 读取之前的行版本信息,以此实现非锁定读取。
Undo Log 的存储构造
- 回滚段与 undo 页
InnoDB 对 Undo Log 的治理采纳段的形式,也就是 回滚段(rollback segment)
。每个回滚段记录了 1024 个 Undo Log segment
,而在每个 Undo Log segment 段中进行Undo 页
的申请。
在 InnoDB1.1 版本
之前(不包含 1.1 版本),只有一个 rollback segment,因而反对同时在线的事务限度为 1024。尽管对绝大多数的利用来说都曾经够用。
从 1.1 版本开始 InnoDB 反对最大128 个 rollback segment
,故其反对同时在线的事务限度进步到了128*1024
。
mysql> show variables like 'innodb_undo_logs';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| innodb_undo_logs | 128 |
+------------------+-------+
尽管 InnoDB1.1 版本反对了 128 个 rollback segment,然而这些 rollback segment 都存储于共享表空间 ibdata 中。从 lnnoDB1.2 版本开始,可通过参数对 rollback segment 做进一步的设置。这些参数包含:
innodb_undo_directory:
设置 rollback segment 文件所在的门路。这意味着 rollback segment 能够寄存在共享表空间以外的地位,即能够设置为独立表空间。该参数的默认值为“./”,示意以后 InnoDB 存储引擎的目录。
innodb_undo_logs:
设置 rollback segment 的个数,默认值为 128。在 InnoDB1.2 版本中,该参数用来替换之前版本的参数 innodb_rollback_segments。
innodb_undo_tablespaces:
设置形成 rollback segment 文件的数量,这样 rollback segment 能够较为均匀地散布在多个文件中。设置该参数后,会在门路 innodb_undo_directory 看到 undo 为前缀的文件,该文件就代表 rollback segment 文件。
- 回滚段与事务
1. 每个事务只会应用一个回滚段(rollback segment),一个回滚段在同一时刻可能会服务于多个事务。
2. 当一个事务开始的时候,会制订一个回滚段,在事务进行的过程中,当数据被批改时,原始的数据会被复制到回滚段。
3. 在回滚段中,事务会一直填充盘区,直到事务完结或所有的空间被用完。如果以后的盘区不够用,事务会在段中申请扩大下一个盘区,如果所有已调配的盘区都被用完,事务会笼罩最后的盘区或者在回滚段容许的状况下扩大新的盘区来应用。
4. 回滚段存在于 Undo 表空间中,在数据库中能够存在多个 Undo 表空间,但同一时刻只能应用一个 Undo 表空间。
5. 当事务提交时,InnoDB 存储引擎会做以下两件事件:
1. 将 Undo Log 放入列表中,以供之后的 purge(荡涤、革除)操作
2. 判断 Undo Log 所在的页是否能够重用(低于 3 / 4 能够重用),若能够调配给下个事务应用
- 回滚段中的数据分类
未提交的回滚数据 (uncommitted undo information):
该数据所关联的事务并未提交,用于实现读一致性,所以该数据不能被其余事务的数据笼罩。
曾经提交但未过期的回滚数据 (committed undo information):
该数据关联的事务曾经提交,然而仍受到 undo retention 参数的放弃工夫的影响。
事务曾经提交并过期的数据 (expired undo information):
事务曾经提交,而且数据保留工夫曾经超过 undo retention 参数指定的工夫,属于曾经过期的数据。当回滚段满了之后,会优先笼罩 ” 事务曾经提交并过期的数据 ”。
- Undo 页的重用
当咱们开启一个事务须要写 Undo log 的时候,就得先去 Undo Log segment
中去找到一个闲暇的地位,当有空位的时候,就去申请 Undo 页,在这个申请到的 Undo 页中进行 Undo Log 的写入。咱们晓得 MySQL 默认一页的大小是16k
。
为每一个事务调配一个页,是十分节约的 (除非你的事务十分长),假如你的利用的 TPS(每秒解决的事务数目) 为 1000,那么 1s 就须要 1000 个页,大略须要 16M 的存储,1 分钟大略须要 1G 的存储。如果照这样上来除非 MySQL 清理的十分勤快,否则随着工夫的推移,磁盘空间会增长的十分快,而且很多空间都是节约的。
于是 Undo 页就被设计的能够重用了 ,当事务提交时,并 不会立即删除Undo 页。因为重用,所以这个 Undo 页可能混杂着其余事务的 Undo Log。Undo Log 在 commit 后,会被放到一个链表中,而后判断 Undo 页的应用空间是否小于 3 /4,如果小于 3 / 4 的话,则示意以后的 Undo 页能够被重用,那么它就不会被回收,其余事务的 Undo Log 能够记录在以后 Undo 页的前面。因为 Undo Log 是离散的,所以清理对应的磁盘空间时,效率不高。
- Undo Log 日志的存储机制
如上图,能够看到,Undo Log 日志外面不仅寄存着数据更新前的记录,还记录着 RowID
、 事务 ID
、回滚指针
。其中事务 ID 每次递增,回滚指针第一次如果是 INSERT 语句的话,回滚指针为 NULL,第二次 UPDATE 之后的 Undo Log 的回滚指针就会指向刚刚那一条 Undo Log 日志,以此类推,就会造成一条 Undo Log 的回滚链,不便找到该条记录的历史版本。
Undo Log 的工作原理
在更新数据之前,MySQL 会提前生成 Undo Log 日志,当事务提交的时候,并不会立刻删除 Undo Log,因为前面可能须要进行回滚操作,要执行回滚(ROLLBACK)操作时,从缓存中读取数据。Undo Log 日志的删除是通过通过后盾 purge 线程进行回收解决的。
- 1、事务 A 执行 UPDATE 操作,此时事务还没提交,会将数据进行备份到对应的 Undo Buffer,而后由 Undo Buffer 长久化到磁盘中的 Undo Log 文件中,此时 Undo Log 保留了未提交之前的操作日志,接着将操作的数据,也就是 test 表的数据长久保留到 InnoDB 的数据文件 IBD。
- 2、此时事务 B 进行查问操作,间接从 Undo Buffer 缓存中进行读取,这时事务 A 还没提交事务,如果要回滚(ROLLBACK)事务,是不读磁盘的,先间接从 Undo Buffer 缓存读取。
Undo Log 的类型
在 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 线程进行最初的删除。
Undo Log 的生命周期
简要生成过程
以下是 Undo+Redo 事务的简化过程:
假如有 2 个数值,别离为 A = 1 和 B =2,而后将 A 批改为 3,B 批改为 4
1. start transaction;
2.记录 A = 1 到 Undo Log;
3. update A = 3;
4.记录 A =3 到 Redo Log;
5.记录 B = 2 到 Undo Log;
6. update B = 4;
7.记录 B = 4 到 Redo Log;
8.将 Redo Log 刷新到磁盘;
9. commit
- 在 1 - 8 步骤的任意一步零碎宕机,事务未提交,该事务就不会对磁盘上的数据做任何影响。
-
如果在 8 - 9 之间宕机。
- Redo Log 进行复原
- Undo Log 发现有事务没实现进行回滚。
- 若在 9 之后零碎宕机,内存映射中变更的数据还来不及刷回磁盘,那么零碎复原之后,能够依据 Redo Log 把数据刷回磁盘。
流程图:
Undo Log 的配置参数
innodb_max_undo_log_size:
undo 日志文件的最大值,默认 1GB,初始化大小 10M
innodb_undo_log_truncate:
标识是否开启主动膨胀 Undo Log 表空间的操作
innodb_undo_tablespaces:
设置独立表空间的个数,默认为 0,标识不开启独立表空间,undo 日志保留在 ibdata1 中
innodb_undo_directory:
undo 日志存储的目录地位
innodb_undo_logs: 回滚的个数 默认 128
参考文章
- 《MySQL 是怎么运行的 – 从根儿上了解 MySQL》—小孩子 4919
Enjoy GreatSQL :)
## 对于 GreatSQL
GreatSQL 是由万里数据库保护的 MySQL 分支,专一于晋升 MGR 可靠性及性能,反对 InnoDB 并行查问个性,是实用于金融级利用的 MySQL 分支版本。
相干链接:GreatSQL 社区 Gitee GitHub Bilibili
GreatSQL 社区:
欢送来 GreatSQL 社区发帖发问
https://greatsql.cn/
技术交换群:
微信:扫码增加
GreatSQL 社区助手
微信好友,发送验证信息加群
。