驗證mysql事務隔離級別機制加鎖情況與MVCC機制


道路越深,即越孤獨。

 

 大家都知道,mysql innodb引擎支持事務,而事務具有ACID四大特性,分別是原子性,一致性,隔離性及持久性。其中事務的隔離性,指的是當多條事務並發時,對事務中的sql指令的一些同步及加鎖約束,隔離性分為四個隔離級別,分別是Read-uncommit,Read-commited,Repeatable-Read,Serializable。

這四種事務隔離級別,分別對應着不同的同步機制,其中,可重復讀和序列化是我們最常接觸到的,而另外兩種相對用的比較少,因此接下來我會通過簡單的實驗來驗證一下

 

可重復讀和序列化事務隔離級別的加鎖情況與MVCC機制。

 

實驗開始前,我們不妨先記一下試驗用的事務分析指令:

show variables like '%iso%';            //查看當前連接使用的事務隔離級別

set global transaction isolation level Repeatable read;  //設置mysql全局事務隔離級別為可重復讀

set session transaction isolation level Repeatable read;  //設置當前會話事務隔離級別為可重復讀

set session transaction isolation level Serializable;  // 設置當前會話事務隔離級別為序列化

show variables like 'AUTOCOMMIT';       //顯示當前會話事務是否自動提交

set AUTOCOMMIT = 'OFF';        //禁止事務自動提交

show processlist;                   // 查看數據庫當前所有連接

SELECT                              
trx_id AS `事務ID`,
trx_state AS `事務狀態`,
trx_requested_lock_id AS `事務需要等待的資源`,
trx_wait_started AS `事務開始等待時間`,
trx_tables_in_use AS `事務使用表`,
trx_tables_locked AS `事務擁有鎖`,
trx_rows_locked AS `事務鎖定行`,
trx_rows_modified AS `事務更改行`
FROM
information_schema.innodb_trx ;                         //查看當前數據庫的所有正在執行的事務

 

有了這些指令后,便可以開始進行實驗了,我們可以先研究一下機制更加簡單的序列化。

一般書籍對序列化定義是:所有事務串行執行。

在樓主未進行實驗前,樓主曾經猜測並深深以為,序列化的事務是通過多線程存入 ,單線程消費阻塞隊列得以實現的 。因為這樣做符合書籍上定義的事務串行執行並且不需要加鎖控制。

這次實驗,證明了樓主曾經的以為是錯的。

序列化事務,其實是通過嚴格的寫鎖控制來實現的。下面是實驗過程

一:序列化事務加鎖機制探究

1.建立測試表serial,包含id、key、value三個字段

 

2.開啟兩條數據庫連接

3.插入幾條數據

 

4.修改數據庫隔離級別為序列化並禁止自動提交

show processlist;
show variables like 'AUTOCOMMIT';
show variables like '%iso%';
set session transaction isolation level Serializable;
set AUTOCOMMIT = 'OFF';

 5.修改記錄

在連接1中執行

UPDATE `test_transaction`.`serial` SET `value`='5' WHERE `id`='1';

然后在連接2中執行
UPDATE `test_transaction`.`serial` SET `value`='6' WHERE `id`='2';

然后在連接1中執行

UPDATE `test_transaction`.`serial` SET `value`='7' WHERE `id`='2';

最后在連接2中執行

UPDATE `test_transaction`.`serial` SET `value`='8' WHERE `id`='1‘;

 

6.總結結論

結果如下

此時發生死鎖。

死鎖的產生,即說明了序列化是通過加鎖的機制實現事務的。與此同時,有

由於產生死鎖的事務自動回滾退出,連接1的事務持有2個行鎖,即id為’1‘、’2‘的兩行記錄。此時在連接1中執行commit,事務結束。

二、可重復讀與MVCC機制探究

1.建立測試表repeatableread,包含id、key、value三個字段

2.開啟兩條數據庫連接

3.插入幾條數據

 

4.修改數據庫隔離級別為可重復讀並禁止自動提交

show processlist;
show variables like 'AUTOCOMMIT';
show variables like '%iso%';
set session transaction isolation level Repeatable read;
set AUTOCOMMIT = 'OFF';

5.實驗研究可重復讀sql行為與MVCC

5.1 在連接1中執行查詢sql

select * from repeatableread where ID='1'; 

此時有

這說明可重復讀的讀操作不加鎖。這就對了,可重復讀的讀操作依靠的MVCC使其對讀操作無需加鎖,只需要讀對應版本號的行數據就可以了

5.2 在連接1中執行更新 sql

UPDATE `test_transaction`.`repeatableread` SET `value`='8' WHERE `id`='1';

此時有

這說明可重復讀的寫操作加鎖,那么寫操作到底加的是寫鎖還是讀鎖呢?

在連接2中執行更新sql

UPDATE `test_transaction`.`repeatableread` SET `value`='8' WHERE `id`='1';

此時有

這就說明可重復讀的寫操作加的是寫鎖了。

5.3.MVCC探究

我們都知道,可重復讀的特點就是在一個事務中重復多讀取同一條記錄的的查詢結果是相同的。

而通過剛才的實驗可知,可重復讀的讀操作不加鎖,而寫操作對行加寫鎖,僅僅依靠這樣的讀寫加鎖機制是無法實現可重復讀的特點的。

那么可重復讀的特點是如何實現的呢?答案就是MVCC機制(多版本並發控制)了。

要理解MVCC機制的話需要引入ReadView的概念,ReadView是什么呢?

 

(引用部分原文  https://blog.csdn.net/qq_38538733/article/details/88902979  ,因為講的實在太清楚了!):

 

ReadView可以理解為一個列表,它記錄着當前正在活躍的事務,我們把這個列表命名為為m_ids

這樣在訪問某條記錄時,只需要按照下邊的步驟判斷記錄的某個版本是否可見:

 

如果被訪問版本的trx_id屬性值小於m_ids列表中最小的事務id,表明生成該版本的事務在生成ReadView前已經提交,所以該版本可以被當前事務訪問。

 

如果被訪問版本的trx_id屬性值大於m_ids列表中最大的事務id,表明生成該版本的事務在生成ReadView后才生成,所以該版本不可以被當前事務訪問。

 

如果被訪問版本的trx_id屬性值在m_ids列表中最大的事務id和最小事務id之間,那就需要判斷一下trx_id屬性值是不是在m_ids列表中,如果在,說明創建ReadView時生成該版本的事務還是活躍的,該版本不可以被訪問;如果不在,說明創建ReadView時生成該版本的事務已經被提交,該版本可以被訪問。

 

如果某個版本的數據對當前事務不可見的話,那就順着版本鏈找到下一個版本的數據,繼續按照上邊的步驟判斷可見性,依此類推,直到版本鏈中的最后一個版本,如果最后一個版本也不可見的話,那么就意味着該條記錄對該事務不可見,查詢結果就不包含該記錄。

在MySQL中,READ COMMITTED和REPEATABLE READ隔離級別的的一個非常大的區別就是它們生成ReadView的時機不同,我們來看一下。

 

READ COMMITTED --- 每次讀取數據前都生成一個ReadView

REPEATABLE READ ---在第一次讀取數據時生成一個ReadView

小貼士: 事務執行過程中,只有在第一次真正修改記錄時(比如使用INSERT、DELETE、UPDATE語句),才會被分配一個單獨的事務id,這個事務id是遞增的。

6.總結結論

通過實驗,我們驗證了可重復讀是通過對寫操作加寫鎖,及通過MVCC機制進行無鎖讀取數據。這樣做,完全避免了讀操作阻塞寫操作,大大地提升了查詢效率,至於該隔離級別並發修改可能引發的更新問題,在業務中我們一般是通過顯式地加鎖來避免。

 

 

(歡迎想要交流的同學加我qq:1363890602,qq群:297572046,備注:編程藝術)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM