MVCC(Multi-Version Concurrency Control)即多版本并发控制
MySQL 的大多数事务型 (如 InnoDB,Falcon 等) 存储引擎实现的都不是简单的行级锁基于提升并发性能的考虑, 他们一般都同时实现了 MVCC 当前不仅仅是 MySQL, 其它数据库系统 (如 Oracle,PostgreSQL) 也都实现了 MVCC 值得注意的是 MVCC 并没有一个统一的实现标准, 所以不同的数据库, 不同的存储引擎的实现都不尽相同
作为 MySQL 中使用最广泛的存储引擎, 本文主要讨论的是 InnoDB 的多版本并发控制的实现
MVCC 优缺点
MVCC 在大多数情况下代替了行锁, 实现了对读的非阻塞, 读不加锁, 读写不冲突缺点是每行记录都需要额外的存储空间, 需要做更多的行维护和检查工作
MVCC 的实现原理
undo log
为了便于理解 MVCC 的实现原理, 这里简单介绍一下 undo log 的工作过程
在不考虑 redo log 的情况下利用 undo log 工作的简化过程为:
序号 | 动作 |
---|---|
1 | 开始事务 |
2 | 记录数据行数据快照到 undo log |
3 | 更新数据 |
4 | 将 undo log 写到磁盘 |
5 | 将数据写到磁盘 |
6 | 提交事务 |
1)为了保证数据的持久性数据要在事务提交之前持久化
2)undo log 的持久化必须在在数据持久化之前, 这样才能保证系统崩溃时, 可以用 undo log 来回滚事务
Innodb 中的隐藏列
Innodb 通过 undo log 保存了已更改行的旧版本的信息的快照
InnoDB 的内部实现中为每一行数据增加了三个隐藏列用于实现 MVCC
列名 | 长度 (字节) | 作用 |
---|---|---|
DB_TRX_ID | 6 | 插入或更新行的最后一个事务的事务标识符。(删除视为更新,将其标记为已删除) |
DB_ROLL_PTR | 7 | 写入回滚段的撤消日志记录(若行已更新,则撤消日志记录包含在更新行之前重建行内容所需的信息) |
DB_ROW_ID | 6 | 行标识(隐藏单调自增 id) |
结构
数据列 | .. | DB_ROW_ID | DB_TRX_ID | DB_ROLL_PTR |
---|
MVCC 工作过程
MVCC 只在 READ COMMITED 和 REPEATABLE READ 两个隔离级别下工作 READ UNCOMMITTED 总是读取最新的数据行, 而不是符合当前事务版本的数据行而 SERIALIZABLE 则会对所有读取的行都加锁
SELECT
InnoDB 会根据两个条件来检查每行记录:
InnoDB 只查找版本 (DB_TRX_ID) 早于当前事务版本的数据行(行的系统版本号<= 事务的系统版本号, 这样可以确保数据行要么是在开始之前已经存在了, 要么是事务自身插入或修改过的)
行的删除版本号 (DB_ROLL_PTR) 要么未定义 (未更新过), 要么大于当前事务版本号(在当前事务开始之后更新的) 这样可以确保事务读取到的行, 在事务开始之前未被删除
INSERT
InnoDB 为新插入的每一行保存当前系统版本号作为行版本号
DELETE
InnoDB 为删除的每一行保存当前的系统版本号作为行删除标识
UPDATE
InnoDB 为插入一行新记录, 保存当前系统版本号作为行版本号, 同时保存当前系统版本号到原来的行作为行删除标识
来源: http://www.jianshu.com/p/a3d49f7507ff