当前位置:Gxlcms > 数据库问题 > MySQL一致性非锁定读原理以及MVCC简介

MySQL一致性非锁定读原理以及MVCC简介

时间:2021-07-01 10:21:17 帮助过:24人阅读

Session A Session B 2 3 4 begin; begin; 5 6 ------------------------------------------------------------------------------------- 7 8 SELECT * FROM t; SELECT * FROM t; 9 10 > empty set > empty set 11 12 13 ------------------------------------------------------------------------------------- 14 15 INSERT INTO t (id) VALUES (1); 16 17 > 1 row affected 18 19 20 SELECT * FROM t; 21 22 ----------- 23 24 | id | 25 26 ----------- 27 28 | 1 | 29 30 ----------- 31 32 ------------------------------------------------------------------------------------- 33 34 SELECT * FROM t; 35 36 > empty set 37 38 ------------------------------------------------------------------------------------- 39 40 COMMIT; 41 42 ------------------------------------------------------------------------------------- 43 44 SELECT * FROM t; 45 46 > empty set 47 48 ------------------------------------------------------------------------------------- 49 50 51 DELETE FROM t WAERE id = 1; 52 53 > 1 row affected 54 55 ------------------------------------------------------------------------------------- 56 57 SELECT * FROM t; 58 59 ----------- 60 61 | id | 62 63 ----------- 64 65 | 1 | 66 67 ----------- 68 69 ------------------------------------------------------------------------------------- 70 71 COMMIT; 72 73 ------------------------------------------------------------------------------------- 74 75 SELECT * FROM t; 76 77 > empty set 78 79 ------------------------------------------------------------------------------------- 80 81 SELECT * FROM t; 82 83 ----------- 84 85 | id | 86 87 ----------- 88 89 | 1 | 90 91 ----------- 92 93 -------------------------------------------------------------------------------------

一致的读取不适用于某些 DDL语句,如:

1) 一致性读不适用于 DROP TABLE ,因为表已经被 InnoDB 销毁了。

2) 一致性读不适用于 ALTER TABLE ,因为 ALTER TABLE 实际是生成一张原始表的临时表,并在构建完成后删除原始表。 在事务中进行一致的读取时,新表中的行不可见,这种情况下事务会返回 ERTABLEDEF_CHANGED 错误(表定义已更改,请重试事务)。

在没有指定 FOR UPDATE 或者 LOCK IN SHARE MODE 的情况下 INSERT INTO ... SELECT ,UPDATE ...(SELECT)和 CREATE TABLE... 等语句中的的读取会有以下差异:

  • 默认情况下,就像 READ COMMITTED 一样,即使在同一事务中,每个一致性读都会建立和读取自己的快照。
  • 如果将 innodblocksunsafe forbinlog 设置为了 enable 并且事务隔离级别不是 SERIALIZABLE,则读操作不会再行上加锁。

2、多版本并发控制


 

上面说的一致性读(consistend read)的主要是基于MVCC实现,而 MySQL 中大多数事务型(如:InnoDB、Falcon 等)存储引擎都同时实现了 MVCC(Multi-Version Concurrency Control) 

当前不仅仅是 MySQL,其它数据库系统(如:Oracle、PostgreSQL)也都实现了 MVCC。值得注意的是 MVCC 并没有一个统一的实现标准,所以不同的数据库,不同的存储引擎的实现都不尽相同。

多版本控制的核心是数据快照,而 InnoDB 则是通过 undo log 来存储数据快照。

下面展示了在不考虑 redo log 的情况下利用 undo log工作的简化过程:

序号    动作
1    开始事务
2    记录数据行数据快照到undo log
3    更新数据
4    将undo log写到磁盘
5    将数据写到磁盘
6    提交事务

1)为了保证数据的持久性数据要在事务提交之前持久化。 2)undo log的持久化必须在在数据持久化之前,这样才能保证系统崩溃时,可以用undo log来回滚事务。

 

2.1、Innodb中的隐藏列

InnoDB 通过 undo log 保存了已更改行的旧版本的信息的快照。InnoDB 的内部实现中为每一行数据增加了三个隐藏列用于实现 MVCC 。

列名        长度(字节)          作用
DBTRXID      6          插入或更新行的最后一个事务的事务标识符。(删除视为更新,将其标记为已删除)
DBROLLPTR    7         写入回滚段的撤消日志记录(若行已更新,则撤消日志记录包含在更新行之前重建行内容所需的信息)
DBROWID      6         行标识(隐藏单调自增id)

MVCC 只在 READ COMMITED 和 REPEATABLE READ 两个隔离级别下工作。READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。

 

2.2 每个操作所做的内容

SELECT

InnoDB 会根据两个条件来检查每行记录:

1) InnoDB 只查找版本(DBTRXID)早于当前事务版本的数据行(行的系统版本号 <= 事务的系统版本号,这样可以确保数据行要么是在开始之前已经存在了,要么是事务自身插入或修改过的)。

2) 行的删除版本号(DBROLLPTR)要么未定义(未更新过),要么大于当前事务版本号(在当前事务开始之后更新的)。这样可以确保事务读取到的行,在事务开始之前未被删除。

事例

当事务 A 执行查询语句时,其查询数据逻辑图

技术图片

 

 

其查找过程如下,首先,获取记录的事务ID(101),比高水位大,不可见,所以取出记录的上一个历史版本,获取其事务ID(102),比高水位大,不可见,再获取记录的上一个历史版本,获取其事务ID(90),比低水位小,可见,所以返回这个记录中的 k 字段的值 1。 此处需要额外关注的是,事务 B 的更新操作,是在当前记录的最新值上更新的,并不是在历史数据上更新的,否则会丢失事务 B 的更新操作。其实,更新数据都是先读后写的,而且这个读,是读的当前值,称为“当前读”。

INSERT

InnoDB 为新插入的每一行保存当前系统版本号作为行版本号。

DELETE

InnoDB 为删除的每一行保存当前的系统版本号作为行删除标识。

UPDATE

InnoDB 为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。

 

 

参考链接:https://juejin.im/entry/5af2597bf265da0b7e0c3e5f

参考链接:https://juejin.im/post/5dbe4c466fb9a0202e166658

MySQL一致性非锁定读原理以及MVCC简介

标签:持久化   height   自己   mvc   展示   version   另一个   str   --   

人气教程排行