图文结合带你搞懂InnoDB MVCC

打印 上一主题 下一主题

主题 536|帖子 536|积分 1608


  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
  • GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。




    • 前情提要
    • 当前读
    • 快照读

  • 什么是MVCC

    • 三个隐藏字段
    • Undo Log回滚日志
    • MVCC版本链
    • ReadView读视图

  • 不同隔离级别下MVCC分析

    • READ-COMMITTED隔离级别
    • REPEATABLE-READ隔离级别

前情提要

事务有四大特性ACID分别是:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
其中隔离性是通过数据库的锁加上MVCC(多版本并发控制)来保证的。
在介绍MVCC之前先来了解一下当前读和快照读。
当前读

当前读读取的是记录的最新版本。同时在读取的时候还要保证其他的并发事务不能更改当前记录,那么当前读会对它要读取的记录进行加锁。不同的操作会加上不同类型的锁,如:SELECT ... LOCK IN SHARE MODE(共享锁),SELECT ... FOR UPDATE、UPDATE、INSERT、 DELETE(排他锁)。
快照读

简单的不加锁的SELECT就是快照读,快照读读取的是快照生成时的数据,不一定是最新的数据,它是不加锁的非阻塞读。而不同隔离级别下,创建快照的时机也不同:

  • READ-COMMITTED(读已提交):事务每次SELECT时创建ReadView
  • REPEATABLE-READ(可重复读):事务第一次SELECT时创建ReadView,后续一直使用
在MySQL默认隔离级别(REPEATABLE-READ)下,快照读保证了数据的可重复读。
什么是MVCC

MVCC全称Multi-Version Concurrency Control,即多版本并发控制。它是一种并发控制的方法,它可以维护一个数据的多个版本,用更好的方式去处理读写冲突,做到即使有读写冲突也能不加锁。MySQL中MVCC的具体实现,还需要依赖于表中的三个隐藏字段、Undo Log日志以及ReadView。
三个隐藏字段
  1. mysql> SHOW CREATE TABLE stu \G;
  2. *************************** 1. row ***************************
  3.        Table: stu
  4. Create Table: CREATE TABLE `stu` (
  5.   `id` int NOT NULL,
  6.   `name` varchar(10) NOT NULL,
  7.   PRIMARY KEY (`id`)
  8. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
  9. 1 row in set (0.00 sec)
  10. mysql> SELECT * FROM stu;
  11. +----+--------+
  12. | id | name   |
  13. +----+--------+
  14. |  1 |   m    |
  15. |  2 |   f    |
  16. +----+--------+
复制代码
当创建了上述这张表后,我们在查看表结构时只能看到id、name字段,实际上除了这两个字段外,InnoDB引擎还自动为我们添加了三个隐藏字段,见下表:
字段含义DB_TRX_ID最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。DB_ROLL_PTR回滚指针,指向这条记录的上一个版本,用于配合Undo Log,指向上一个版本。DB_ROW_ID隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。我们可以使用ibd2sdi工具来从表空间文件中提取序列化的字典信息(SDI),来验证一下这三个隐藏字段是否存在。
  1. ["ibd2sdi"
  2. ,
  3. {
  4.         "type": 1,
  5.         "id": 402,
  6.         "object":
  7.                 {
  8.     "mysqld_version_id": 80025,
  9.     "dd_version": 80023,
  10.     "sdi_version": 80019,
  11.     "dd_object_type": "Table",
  12.     "dd_object": {
  13.         "name": "stu",
  14.         "mysql_version_id": 80025,
  15.         "created": 20220919023413,
  16.         "last_altered": 20220919023413,
  17.         "hidden": 1,
  18.         "options": "avg_row_length=0;encrypt_type=N;explicit_encryption=0;key_block_size=0;keys_disabled=0;pack_record=1;stats_auto_recalc=0;stats_sample_pages=0;",
  19.         "columns": [
  20.             {
  21.                 "name": "id",
  22.                 "type": 4,
  23.                 "is_nullable": false,
  24.                 "is_zerofill": false,
  25.                 "is_unsigned": false,
  26.                 "is_auto_increment": false,
  27.                 "is_virtual": false,
  28.                 "hidden": 1,
  29. ···省略
  30.             },
  31.             {
  32.                 "name": "name",
  33.                 "type": 16,
  34.                 "is_nullable": false,
  35.                 "is_zerofill": false,
  36.                 "is_unsigned": false,
  37.                 "is_auto_increment": false,
  38.                 "is_virtual": false,
  39.                 "hidden": 1,
  40. ···省略
  41.             },
  42.             {
  43.                 "name": "DB_TRX_ID", #最近修改事务ID
  44.                 "type": 10,
  45.                 "is_nullable": false,
  46.                 "is_zerofill": false,
  47.                 "is_unsigned": false,
  48.                 "is_auto_increment": false,
  49.                 "is_virtual": false,
  50.                 "hidden": 2,
  51. ···省略
  52.             },
  53.             {
  54.                 "name": "DB_ROLL_PTR", #回滚指针
  55.                 "type": 9,
  56.                 "is_nullable": false,
  57.                 "is_zerofill": false,
  58.                 "is_unsigned": false,
  59.                 "is_auto_increment": false,
  60.                 "is_virtual": false,
  61.                 "hidden": 2,
  62. ···省略
  63.             }
  64.         ],
复制代码
注意:因为这张表里已经指定了主键为id列,所以不会生成隐藏主键DB_ROW_ID列。
Undo Log回滚日志

回滚日志,在增、改、删操作的时候产生的便于数据回滚的日志。当INSERT操作的时候,产生的回滚日志在事务提交后可被立即删除。而UPDATE和DELETE操作的时候,产生的Undo Log日志不仅在进行数据回滚时需要,在进行快照读时也需要,所以不会立即被删除。
Undo Log详情可见文章:待浩源Undo Log文章发表后添加
MVCC版本链

当有多个并发事务操作一行数据时,对这行数据的修改会产生多个版本,多个版本通过上述的一个隐藏字段DB_ROLL_PTR回滚指针指向Undo Log数据地址形成一个链表,即MVCC版本链。

ReadView读视图

ReadView读视图是快照读SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
上面讲过Undo Log和MVCC版本链,一条数据经过多次修改会产生多个版本,而快照读是根据不同时机创建的快照获取数据的,那么快照读SQL在执行时该读取那个版本的数据就是靠ReadViw读视图来决定的。
ReadView读视图中包含了四个核心字段,也是读取数据的判断依据:
字段含义m_ids当前活跃的事务ID集合min_trx_id最小活跃事务IDmax_trx_id预分配事务ID,当前最大事务ID+1(因为事务ID是自增的)creator_trx_idReadView创建者的事务IDReadView一共有四种匹配规则:
[table][tr]条件能否访问说明[/tr][tr][td]trx_id == creatro_trx_id[/td][td]可以访问该版本[/td][td]成立,说明数据是当前这个事务更改的。[/td][/tr][tr][td]trx_id < min_trx_id[/td][td]可以访问该版本[/td][td]成立,说明数据已经提交了。[/td][/tr][tr][td]trx_id > max_trx_id[/td][td]不可以访问该版本[/td][td]成立,说明该事务是在ReadView生成后才开启的。[/td][/tr][tr][td]min_trx_id

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊落一身雪

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表