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存储引擎
上一篇: Tornado gen简介
下一篇: Spring batch的自动运行