InnoDB多版本控制

概念:

    多版本控制:MVCC,也就是一致性非锁定读的具体实现方式。

    具体实现方式为:如果读取的行正在执行排他锁类操作(update、delete等),该查询则不会等待锁的释放,而是读取该行的一个快照数据。

    问题来了,什么叫做行快照数据呢?

    实际就是当前行数据的历史版本数据。

    下面我们就来通过示例看下MVCC的实现。

1.REPEATABLE-READ下的MVCC

    在之前的InnoDB共享锁和排他锁示例中,我们可以看到共享锁与排他锁是不兼容的,当然我们之前是先执行共享锁再执行排他锁示例的,读者可以试下先执行排他锁再执行共享锁。结果也会是一致的。这里就不再演示。

    我们当前示例与之前有什么不同呢?

    1)session1(正常查询)

set autocommit=0;##首先就是关闭自动提交
select * from city where id = 1; ##查询一行记录
// res 结果如下,session2中我们要修改其Name
ID	Name	CountryCode	District	Population
1	Kabul	AFG	Kabol	1780000
    
// 查询完成之后,等待session2的执行    

    2)session2(排他锁)

set autocommit=0;##首先就是关闭自动提交
update city set name ='KABUL' where id =1; ##修改id=1这条记录的name

// 这个时候再次执行session1中的查询,发现name没有变化,这个是正常的,因为我们当前事务还没有提交
select * from information_schema.INNODB_TRX; ##查询事务信息
// res 可以看到有两个事务都在运行
trx_id	trx_state	trx_started	trx_requested_lock_id	trx_wait_started	trx_weight	trx_mysql_thread_id	trx_query	trx_operation_state	trx_tables_in_use	trx_tables_locked	trx_lock_structs	trx_lock_memory_bytes	trx_rows_locked	trx_rows_modified	trx_concurrency_tickets	trx_isolation_level	trx_unique_checks	trx_foreign_key_checks	trx_last_foreign_key_error	trx_adaptive_hash_latched	trx_adaptive_hash_timeout	trx_is_read_only	trx_autocommit_non_locking
462617	RUNNING	2019-12-28 18:53:55			3	9			0	1	2	1136	1	1	0	REPEATABLE READ	1	1		0	0	0	0
284711444560536	RUNNING	2019-12-28 18:50:17			0	10			0	0	0	1136	0	0	0	REPEATABLE READ	1	1		0	0	0	0
    
// 执行commit,commit之后再查询事务就只有一个了
commit;

// 再执行session1中的查询数据,发现name还是没有变化,这个时候感觉有点诡异了

    session2中的update事务明明已经提交了,为什么session1还是无法读取到呢。我们再开启一个session3来看下

    3)session3(正常查询)

select * from city where id = 1;
// res
ID	Name	CountryCode	District	Population
1	KABUL	AFG	Kabol	1780000

// 查询事务隔离级别
select @@tx_ISOLATION;
// res
@@tx_ISOLATION
REPEATABLE-READ

    发现name值确实变化了。

    有点奇怪了,我们能从这个示例中总结出什么呢?

    在REPEATABLE-READ隔离级别下的MVCC,select读取的还是事务开始之前行数据。

    

2.READ-COMMITTED下的MVCC

    我们再来看下如果我们修改了隔离级别,会与之前的有区别吗

    下面的示例基本与上述一致,主要区别就是要先修改当前会话事务隔离级别

    1)session1(正常查询)

set autocommit=0;##首先就是关闭自动提交
set session tx_isolation ='read-committed';##修改当前session事务隔离级别为read-committed
select @@tx_ISOLATION;##查询下是否修改正常

select * from city where id = 1; ##查询一行记录
// res 结果如下,session2中我们要修改其Name
ID  Name    CountryCode District    Population
1   KABUL   AFG Kabol   1780000
    
// 查询完成之后,等待session2的执行    

    2)session2(排他锁)

set autocommit=0;##首先就是关闭自动提交
set session tx_isolation ='read-committed';##修改当前session事务隔离级别为read-committed
select @@tx_ISOLATION;##查询下是否修改正常

update city set name ='kabul' where id =1; ##修改id=1这条记录的name

// 这个时候再次执行session1中的查询,发现name没有变化,这个是正常的,因为我们当前事务还没有提交
select * from information_schema.INNODB_TRX; ##查询事务信息
// res 可以看到有两个事务都在运行
trx_id	trx_state	trx_started	trx_requested_lock_id	trx_wait_started	trx_weight	trx_mysql_thread_id	trx_query	trx_operation_state	trx_tables_in_use	trx_tables_locked	trx_lock_structs	trx_lock_memory_bytes	trx_rows_locked	trx_rows_modified	trx_concurrency_tickets	trx_isolation_level	trx_unique_checks	trx_foreign_key_checks	trx_last_foreign_key_error	trx_adaptive_hash_latched	trx_adaptive_hash_timeout	trx_is_read_only	trx_autocommit_non_locking
462619	RUNNING	2019-12-28 19:16:27			3	9			0	1	2	1136	1	1	0	READ COMMITTED	1	1		0	0	0	0
284711444560536	RUNNING	2019-12-28 19:15:05			0	10			0	0	0	1136	0	0	0	READ COMMITTED	1	1		0	0	0	0

// 执行commit,commit之后再查询事务就只有一个了
commit;

// 再执行session1中的查询数据,发现name已经发生变化了

    我们在session1中再次查询该数据的时候,发现name已经被修改了。这个与之前1中REPEATABLE-READ中的结果值很不一样。

    在READ-COMMITTED隔离级别下的MVCC,select读取的被锁定行的最新的一份快照数据。
 

    

总结:

    通过上述示例,我们看到了MVCC在不同隔离级别下的读取方式有所不同,再来总结下:

    在READ-COMMITTED隔离级别下的MVCC,select读取的被锁定行的最新的一份快照数据。

    在REPEATABLE-READ隔离级别下的MVCC,select读取的还是事务开始之前行数据。

参考:MySQL技术内幕 InnoDB存储引擎

发布了122 篇原创文章 · 获赞 119 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_26323323/article/details/103752355