时间:2021-07-01 10:21:17 帮助过:24人阅读
一致的读取不适用于某些 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... 等语句中的的读取会有以下差异:
上面说的一致性读(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来回滚事务。
InnoDB 通过 undo log 保存了已更改行的旧版本的信息的快照。InnoDB 的内部实现中为每一行数据增加了三个隐藏列用于实现 MVCC 。
列名 长度(字节) 作用 DBTRXID 6 插入或更新行的最后一个事务的事务标识符。(删除视为更新,将其标记为已删除) DBROLLPTR 7 写入回滚段的撤消日志记录(若行已更新,则撤消日志记录包含在更新行之前重建行内容所需的信息) DBROWID 6 行标识(隐藏单调自增id)
MVCC 只在 READ COMMITED 和 REPEATABLE READ 两个隔离级别下工作。READ UNCOMMITTED 总是读取最新的数据行,而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。
InnoDB 会根据两个条件来检查每行记录:
1) InnoDB 只查找版本(DBTRXID)早于当前事务版本的数据行(行的系统版本号 <= 事务的系统版本号,这样可以确保数据行要么是在开始之前已经存在了,要么是事务自身插入或修改过的)。
2) 行的删除版本号(DBROLLPTR)要么未定义(未更新过),要么大于当前事务版本号(在当前事务开始之后更新的)。这样可以确保事务读取到的行,在事务开始之前未被删除。
事例
当事务 A 执行查询语句时,其查询数据逻辑图
其查找过程如下,首先,获取记录的事务ID(101),比高水位大,不可见,所以取出记录的上一个历史版本,获取其事务ID(102),比高水位大,不可见,再获取记录的上一个历史版本,获取其事务ID(90),比低水位小,可见,所以返回这个记录中的 k 字段的值 1。 此处需要额外关注的是,事务 B 的更新操作,是在当前记录的最新值上更新的,并不是在历史数据上更新的,否则会丢失事务 B 的更新操作。其实,更新数据都是先读后写的,而且这个读,是读的当前值,称为“当前读”。
InnoDB 为新插入的每一行保存当前系统版本号作为行版本号。
InnoDB 为删除的每一行保存当前的系统版本号作为行删除标识。
InnoDB 为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。
参考链接:https://juejin.im/entry/5af2597bf265da0b7e0c3e5f
参考链接:https://juejin.im/post/5dbe4c466fb9a0202e166658
MySQL一致性非锁定读原理以及MVCC简介
标签:持久化 height 自己 mvc 展示 version 另一个 str --