乐趣区

关于mysql:MySql-MVCC

MVCC

Multiversion Concurrency Control,象征多版本并发管制,也就是 MySql InnoDB 引擎解决幻读问题的计划

MySql InnoDB 默认隔离级别是可反复读,本文后续会围绕 MVCC 开展,简述 MVCC 解决该隔离级别问题的原理 & 形式

相干知识点链接

数据库隔离级别 -> 内容如题
MySql 共享锁 & 排他锁 -> 讲述 SQL 层面会用到的 2 种锁
MySql 行级锁 & 表级锁 -> 讲述不同存储引擎的锁类型

快照读

快照读又称为一致性读,指读取的是快照数据,MySql InnoDB 中不加锁的 SELECT 都属于快照读,即非阻塞读

SELECT * FROM table_name WHERE

上文知识点中会提到,倒退出快照读是为了进步并发性能,快照读的实现是基于 MVCC,防止了加锁操作从而升高了开销;在多版本控制中,快照读并不一定是最新版本的数据,可能是历史版本;快照读的前提是隔离级别可反复读,串行化级别下会进化成以后读

以后读

以后读获取的是最新数据,读取时要保障其余并发事务不能批改以后记录,会对读取的记录进行加锁,加锁语句可在上述链接 共享锁 & 排他锁 中查看

以后读带来的问题

事务 A

select * from table where column > 1 for update

事务 B

insert into table(column) values (2);
insert into table(column) values (3);

事务 A 在事务 B 之前应用以后读获取数据,事务 B 在事务 A 之后进行数据写入,在主库中执行此命令可能不会有问题,然而如果事务 B 的数据在事务 A 完结之前写入结束,从库通过 binlog 同步,则在从库中事务 A 产生的改变可能对事务 B 的数据失效,导致主库从库数据不统一;

间隙锁

对于上述问题,MySql 引入了间隙锁来解决,其原理为,在事务 A 加锁是时,MySql 会对其搜寻的数据的间隙加上锁,即对所有 column > 1 的数据加锁,即便数据不存在,或者说,不容许任何事务对
column > 1 进行写入,此时就能阻止事务 B 的执行,从而保障了主库从库数据统一

间隙锁带来的问题

在上述案例中,如果事务 A 迟迟不执行结束,会阻塞所有写入 column > 1 数据的事务,此时并发效率是非常低的,如果锁住的数据量过大,都会导致一系列问题,在高并发的环境下是无奈承受的,因为在 MySql InnoDB 中,默认是敞开间隙锁的,应用须要独自开启

innodb_locks_unsafe_for_binlog

快照读解决的问题

承接上述,在以后读带来的一些列问题,为了提高效率,MySql 引入了快照的概念;简述而言,在事务开始时,MySql 会给影响到的数据加一个版本号(trx_id),这个版本号即为以后事务 id,当后续有其余事务对数据进行操作,版本号会递增,在后面的事务查问时,只会匹配到版本号等于或小于以后事务 id 的数据,因而无奈读取到后续事务写入的数据,从而解决幻读,与不可反复读的问题;快照读的实现,次要依赖于版本链 & Undo & Read View

版本链

InnoDb 会给数据退出三个暗藏列

// 事务标识,也能够了解为版本号
1.trx_id
// 回滚指针,指向这条记录的上一个版本
2.roll_pointer
// 当表不存在主键时,默认退出,用来标记数据是否删除,与本文关联不大,后续不再讲述
3.row_id

由 trx_id 来标记数据的版本,即上一次事务的版本号;每次对数据进行改变,都会记录一条 undo 日志,每条 undo 日志都有一个 roll_pointer 属性(Insert 操作对应的 undo 日志没有该属性,因为该数据并没有更早的版本);随着更新次数的增多,所有的版本都会被 roll_pointer 连接成一个链表,这个链表称为版本链,版本链的头节点就是以后记录最新的值;另外,每个版本中蕴含生成该版本对应的事务 id(trx_id,即以后数据的版本号),在事务中判断版本可见性的时应用

Undo log

用于记录数据被批改之前的日志 (即数据历史版本),在批改之前会先把数据写入 undo log 中,当事务进行回滚时能够通过 undo log 中的日志进行数据还原;undo log 保障事务进行 rollback 时的原子性和一致性,事务进行回滚的时候应用 undo log 的数据进行复原;在 MVCC 中,通过读取 undo log 的版本号(trx_id) 实现不同事务读取本人可见的数据;undo log 次要分为两种

// 事务在 insert 时产生的 undo log,只在事务回滚时须要,在事务提交后即可抛弃
1.insert undo log
// 事务在 update & delete 时产生的 undo log,在事务回滚 & 快照读时须要
2.update undo log(次要)

事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统以后的一个快照。

记录并保护零碎以后沉闷事务的 ID(trx_id)(没有 commit,当每个事务开启时,都会被调配一个 ID, 这个 ID 是递增的,所以越新的事务,ID 值越大),是零碎中以后不应该被本事务看到的其余事务 id 列表。

Read View

当事务执行快照读的时候,对该记录创立 Read View(读视图),用它作为条件判断以后事务能看到的数据版本;可能是以后最新的数据,也有可能是该行记录的 undo log 外面的某个版本的数据;即 trx_id 小于或等于以后事务 id 的数据

Read View 的属性

// 以后零碎沉闷事务 id 汇合(即未提交的事务)
1.trx_ids
// 创立以后 Read View 时,以后零碎最新事务 id+1,即新事务版本号比之前的大
2.low_limit_id
// 创立以后 Read View 时,未提交的事务最小的 id
3.up_limit_id
// 创立以后 Read View 的事务 id
4.creator_trx_id

可见判断

1.Read View 中,数据版本号 (trx_id) 小于以后零碎未提交事务的最小 id,则阐明该数据在以后所有未提交事务之前就已生成,故可见
2.Read View 中,数据版本号(trx_id) 等于以后事务 id,则阐明该数据由以后数据创立,故可见
3.Read View 中,数据版本号(trx_id) 小于以后事务 id,且该数据版本号 (trx_id) 不存在于沉闷事务 id 中 (trx_ids),则阐明操作该数据的事务已提交,故可见
4.Read View 中,数据版本号(trx_id) 小于以后事务 id,且该数据版本号 (trx_id) 存在于沉闷事务 id 中 (trx_ids),则阐明操作该数据的事务未提交,故不可见
5.Read View 中,数据版本号(trx_id) 大于以后事务 id,则阐明该数据是以后事务之后的事务创立的,故不可见

参考资料:
https://blog.csdn.net/SeekN/a…
https://baijiahao.baidu.com/s…
https://baike.baidu.com/item/…
https://blog.csdn.net/zuodaoy…

退出移动版