MySQL之InnoDB中的MVCC用法
1、背景
MVCC叫做多版本并發(fā)控制,通過維護數(shù)據(jù)的多個歷史版本實現(xiàn)讀寫分離:讀操作訪問快照版本,無需加鎖,避免阻塞寫操作;寫操作創(chuàng)建新版本,不影響其它事務的讀操作。
這種機制支持了讀已提交和可重復讀兩種事務隔離級別,InnoDB中是通過隱藏列事務id、版本鏈、Read View實現(xiàn)的MVCC。
2、設置事務的隔離級別
設置事務隔離級別可以通過修改配置文件和通過sql語句,修改的隔離級別有4種:讀未提交、讀已提交、可重復讀、串行化,修改配置文件可以永久生效,查看配置文件中的隔離級別如下:
[root@xxx xxx]# cat /xxx/my.cnf | grep 'transaction_isolation' transaction_isolation = READ-COMMITTED
通過sql語句修改是臨時生效的,有3種修改方式,一個是會話級設置,只影響當前連接,例如:
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.00 sec)
另一個是全局設置,影響所有連接,例如:
mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; Query OK, 0 rows affected (0.00 sec)
最后一個是僅對下一個事務生效,例如:
mysql> SET TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.00 sec)
3、MVCC
【1】版本鏈
之前講過隱藏列:row_id、trx_id、roll_pointer,其中row_id不一定存在,當沒有主鍵和唯一索引時,row_id才存在,trx_id是事務id,roll_pointer指向undo日志,通過roll_pointer就可以組成一條版本鏈,接下來通過如下表和數(shù)據(jù)來說明:
mysql> show create table student; +---------+------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------+ | Table | Create Table | +---------+------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------+ | student | CREATE TABLE `student` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL DEFAULT '' COMMENT '姓名', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci | +---------+------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> select * from student; +----+------+ | id | name | +----+------+ | 1 | 張三 | +----+------+ 1 row in set (0.00 sec)
接下來分別在兩個事務中更新數(shù)據(jù),假設事務id分別為20和50:
時間順序 | 事務A,事務id=20 | 事務B,事務id=50 |
---|---|---|
t1 | 開始事務 | 開始事務 |
t2 | UPDATE student SET name = ‘李四’ WHERE id = 1; | |
t3 | 提交事務 | |
t4 | UPDATE student SET name = ‘王五’ WHERE id = 1; | |
t5 | 提交事務 |
此時版本鏈如下:
name為王五的代表最新記錄,name為李四和張三的為undo日志。
【2】ReadView
- 對于未提交讀的隔離級別事務來說,由于可以讀到未提交事務修改的記錄,直接讀取最新的版本就好了;
- 對于串行化隔離級別的事務來說是使用加鎖的方式來訪問記錄的;
- 對于讀已提交和可重復讀隔離級別的事務來說,只能讀到已提交事務的結(jié)果。所以核心問題就是版本鏈中哪一個版本對當前事務是可見的,所有就有了ReadView,ReadView中包含4個部分:
名稱 | 含義 |
---|---|
m_ids | 生成ReadView時當前系統(tǒng)中活躍的讀寫事務id列表 |
min_trx_id | m_ids中的最小值 |
max_trx_id | 分配給下一個事務的id值,不是m_ids中的最大值 |
creator_trx_id | 生成ReadView的事務id,只讀事務中為0 |
通過ReadView的屬性,我們就能判斷對記錄版本鏈中的哪一個版本可見,判斷規(guī)則如下:
1、如果被訪問版本的trx_id與ReadView的中的creator_trx_id相同,說明當前事務再訪問自己修改的記錄,所以該版本可以被當前事務訪問。
2、如果被訪問版本的trx_id小于ReadView的中的min_trx_id值,說明生成該版本的事務在當前事務之前已經(jīng)提交,所以該版本可以被當前事務訪問。
3、如果被訪問版本的trx_id大于ReadView的中的max_trx_id值,表明生成該版本的事務在當前事務之后開啟,所以該版本不可以被當前事務訪問。
4、如果被訪問的版本的trx_id在ReadView的min_trx_id和max_trx_id之間,需要判斷trx_id是否在m_ids中,在就說明該版本的事務還是活躍的,不可以被訪問;不在就說明該版本已經(jīng)提交,可以被訪問。
如果某個版本的數(shù)據(jù)對當前事務不可見,就順著版本鏈去判斷下一個版本的數(shù)據(jù),一直到最后一個版本,如果最后一個版本也不可見,那查詢結(jié)果就不包含這條記錄。
【3】ReadView的生成時機
隔離級別 | 生成ReadView時機 |
---|---|
讀已經(jīng)提交 | 每次讀取數(shù)據(jù)前都生成一個ReadView |
可重復讀 | 第一次讀取數(shù)據(jù)時生成一個ReadView |
4、總結(jié)
MVCC是一種數(shù)據(jù)庫并發(fā)控制技術(shù),通過維護數(shù)據(jù)的多個歷史版本實現(xiàn)讀寫操作的并行化,從而提升性能并解決事務隔離性問題。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Mysql通過explain分析定位數(shù)據(jù)庫性能問題
這篇文章主要介紹了Mysql通過explain分析定位數(shù)據(jù)庫性能問題,明確SQL在Mysql中實際的執(zhí)行過程是怎樣的,如果查詢字段沒有索引則增加索引,如果有索引就要分析為什么沒有用到索引,本文詳細講解,需要的朋友可以參考下2023-01-01用SELECT... INTO OUTFILE語句導出MySQL數(shù)據(jù)的教程
這篇文章主要介紹了用SELECT... INTO OUTFILE語句導出MySQL數(shù)據(jù)的教程,是MySQL入門學習中的基礎知識,需要的朋友可以參考下2015-05-05