欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

InnoDB多版本控制

程序员文章站 2022-06-14 18:57:29
...

概念:

    多版本控制: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存储引擎