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