【MySQL进阶-05】深入理解mvcc机制(详解)
MySql系列整體欄目
| 【一】深入理解mysql索引本質 | https://blog.csdn.net/zhenghuishengq/article/details/121027025 |
| 【二】深入理解mysql索引優化以及explain關鍵字 | https://blog.csdn.net/zhenghuishengq/article/details/124552080 |
| 【三】深入理解mysql的索引分類,覆蓋索引(失效),回表,MRR | https://blog.csdn.net/zhenghuishengq/article/details/128273593 |
| 【四】深入理解mysql事務本質 | https://blog.csdn.net/zhenghuishengq/article/details/127753772 |
| 【五】深入理解mvcc機制 | https://blog.csdn.net/zhenghuishengq/article/details/127889365 |
| 【六】深入理解mysql的內核查詢成本計算 | https://blog.csdn.net/zhenghuishengq/article/details/128820477 |
| 【七】深入理解mysql性能優化以及解決慢查詢問題 | https://blog.csdn.net/zhenghuishengq/article/details/128854433 |
| 【八】深入理解innodb和buffer pool底層結構和原理 | https://blog.csdn.net/zhenghuishengq/article/details/128993871 |
| 【九】深入理解mysql執行的底層機制 | https://blog.csdn.net/zhenghuishengq/article/details/128100377 |
| 【十】深入理解mysql集群的高可用機制 | https://blog.csdn.net/zhenghuishengq/article/details/126239652 |
| 【彩蛋篇】深入理解順序io和隨機io | https://blog.csdn.net/zhenghuishengq/article/details/129080088 |
深入理解mvcc機制
- 一,MVCC定義
- 1,undolog日志
- 2,undolog版本控制鏈
- 3,readView
- 3.1,readview簡介
- 3.2,readview和undolog結合使用規則
- 3.3,readview和undolog基本使用
- 4,總結
一,MVCC定義
MVCC:Multi-Version Concurrency Control,多版本并發控制機制。
在mysql中,為了滿足事務的四大特性之一的隔離性,就是當前事務中的查詢的數據不受其他事務的增刪改操作的影響,因此mysql主要是通過這個可串行化的這種隔離級別和現在即將要談的mvcc機制來實現。而可串行化就是將所有的操作由并行改為串行,就是在每個增刪改包括查操作上面都加了鎖,因此性能非常的低,因此mysql也并沒有選擇這個可串行化來作為mysql的默認的隔離級別,而是使用的可重復讀。接下來就是主要談一下這個可重復讀事務中的mvcc的機制和底層原理。
1,undolog日志
在講mvcc機制之前,需要先了解一下這個undolog日志。在mysql中,如果使用的是默認的可重復讀的這個隔離級別,在一條更新語句中如果加了事務,那么在這個事務啟動之后,提交之前,那么這條數據是暫時不會添加到數據庫的,直到事務提交成功才會提交或者更新到數據庫。那么中間就需要實現數據的暫存,那么這種存儲的方式就是通過這個undolog日志的的是實現的。
CREATE TABLE `product` (`id` bigint(20) NOT NULL,`product_id` int(11) DEFAULT NULL COMMENT '商品id',`version` int(11) DEFAULT NULL COMMENT '版本',`stock` int(11) DEFAULT NULL COMMENT '商品數量',`updated_time` datetime DEFAULT NULL COMMENT '更新時間',`created_time` datetime DEFAULT NULL COMMENT '創建時間',`is_deleted` tinyint(4) DEFAULT NULL COMMENT '是否刪除',PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;如上,創建一張表,然后里面新增一條數據
insert into stock values (1,1,0,100,now(),now(),0);拿一條更新語句來說,如上面一張商品表,接下來要扣減一件商品的庫存,一開始這件商品有100,那么現在扣減20,如果扣減成功,那么數據庫的值就是80
update product set stock = stock - 20 where id = 1;如果在扣減數據時,發生異常出現回滾,那么此時就需要回滾成之前的值,就是需要一個日志來記錄扣減之前的值,那么就是通過這個undolog來記錄的。就是說在這個update更新語句中,在開啟事務之后,提交事務之前,這個庫存100就會記錄在undolog日志里面,減完后的80這個值如果整個事務沒有出現異常那么就直接加入到數據庫里面,如果出現異常那么就將undolog里面的值作為回滾數據。一句話說這個undolog日志就是用來記錄被修改的值,防止出現異常回滾的
2,undolog版本控制鏈
當然這個undolog日志也不是只記錄一條,如在一個或者多個事務中對這個庫存進行了多次的修改,那么這個undolog就會形成一條歷史版本控制鏈。在這個版本控制鏈中,有一個隱藏的事務id和指針。事務id在新增或者更新都會生成一個事務id,默認自增(重點);指針所指向的就是當前數據修改前的一個歷史數據,如果出現了回滾,那么就會根據這個鏈路依次的往前回滾,直到找到上一個或者前面幾個。
這個事務id是在sql更新或者新增的時候生成,并非事務提交的時候生成,因此可能出現事務大的id先提交,那么版本控制鏈路里面的事務id的大小就是亂序的。
3,readView
3.1,readview簡介
在上面的undolog日志里面,可以發現確實時記錄了所有的修改的值,也知道這個undolog是用來回滾的,但是會存在一個問題,如果單純使用一個undolog來解決這個回滾問題,那么就會不知道回滾到鏈路中的哪一個結點,因此就需要引入這個readView來和這個undolog結合使用,通過readview來知道需要回滾到鏈路的哪一個結點。
在一個事務里面,執行任何查詢都會生成當前事務的一致性視圖read-view,在可重復讀中的事務隔離級別中,該視圖在事務結束之前都不會變化。當然如果是讀已提交的隔離級別那么在每次執行sql時都會重新生成視圖。
在可重復讀的事務里面,這個readview視圖由未提交的事務id數組和已創建的最大事務id(max_id)組成,因此這個最大的 max_id 可能在數組里面,也可能不在,因為事務最大的id可能先提交,而數組里面的id都是未提交的。
[trx_id1,trx_id2],max_id如上圖,有四個事務ABCD,同時開啟事務,同時去操作這個商品表的庫存,事務ABC在執行更新語句之后,就會產生一個事務id,因為事務id是自增的,因此從左往右事務依次遞增。而事務D里面主要是用來查詢,并無增刪改操作,主要是查詢當前事務中的庫存的數量,由于沒有更新和查詢語句,因此也沒有事務id。由于三個事務是同時開始,因此commit提交的時間取決于更新語句的時間,誰先更新完誰先提交,因此可能會出現事務大的id先提交。
上圖主要是針對庫存表中id為1的那一行數據進行操作的,而id=2只是為了通過更新語句給這個事務生成一個事務id。
并且mvcc機制主要是針對于可重復讀的這個隔離級別,因此在D中暫時只考慮查詢有其他事務提交的數據,未提交之前的數據暫時不做select查詢考慮
3.2,readview和undolog結合使用規則
在使用這個readview只能看到當前的幾個事務,并且不能得知事務的提交順序,因此需要結合上面所說的undolog一起使用。在兩者結合使用之前,需要有一個readview視圖和undolog版本的比對規則,接下來先詳細的說一下這個比對規則的一些命名:假設這個數組中未提交的的事務id數組的最小值假設為min_id,已創建事務的事務id的最大id為max_id,undolog版本控制鏈的頭結點為head結點。如在下面的第三個select查詢語句中,min_id = 102 , max_id = 104。
然后以這個min_id和這個max_id為分界處,小于這個min_id的為已提交事務,在這兩個值區間的為未提交或者已提交事務,大于這個max_id的,為未開始事務。
那么規則如下:
head結點的事務id <= min_Id:已提交的事務,該事務是可見的
min_Id < head結點的事務id <= max_Id:未提交的事務或者已提交的事務
? 如果事務id在數組中:表示事務未提交,不可見
? 如果事務id不在數組中:那么表示已提交,是可見的
max_Id < head結點的事務id : 未開始的事務,不可見
總結:只要滿足一個事務是可見的,那么這個版本控制鏈路對應結點的值就是需要找的值。
3.3,readview和undolog基本使用
1,假設庫存一開始為200,由于事務id是自增,那么可以暫時假設這個事務trx_id=101的值對應的庫存就是200,那么在事務D中,在第一次查詢之后就會生成一個readview視圖,并且在事務提交之前,這個視圖的值不會改變。接下來主要研究一下在這個RR的默認級別事務中,為何select查詢的值可以不變化,以及readview和undolog匹配的過程是咋樣的。
2,接下來看第一個select查詢,此時事務ABC都因為有了更新語句,因此此時abc都有對應的事務,并且事務A已經提交事務,此時的事務readview組成如下,而undolog鏈路中的值如下圖,因為主要是針對表中id為1的庫存對應的版本鏈路,因此暫時只有兩個數據,bc中兩個更新語句只為了生成事務id,數據并不在一個undolog版本鏈路上,并且此時頭節點head對應的事務id為102。
那么通過這個頭結點head對應的事務和readview的視圖進行對比,此時的head事務id為102,min_id為數組中最小值103,max_id為已創建的最大值id104,根據版本比對規則,符合第一條head結點的事務id小于min_id,即當前結點時可見的,只要獲取到的值是可見的,那么查詢到的值就是這個事務對應的值,即100。
//第一個select查詢語句的值,由未提交的事務id數組和已創建的最大的事務id組成 [103,104],1043,接下來看第二個select查詢語句,此時的事務B提交了,事務B是操作id為1的商品數據,因此在更新時會將原始值加入到這個undolog的日志版本鏈路上。由于事務D并沒有提交,因此此時的readview如下,和之前一樣,但是undolog日志鏈路會多一條數據,其鏈路如下圖
那么此時的頭節點的值為事務id103,即head對應的事務id為103,min_id為103,max_id為104。根據版本對比規則,符合第二條,但是此時的head對應的事務id還在數組中,因此這個結點的數據并不可見。那么將繼續對比下一個結點,下一個結點的事務id為102,符合第一條head結點的事務id小于min_id,即事務id為102對應的結點時可見的,那么查詢到的值仍然時100
//第二個select查詢語句的值,由未提交的事務id數組和已創建的最大的事務id組成 [103,104],1044,接下來再看第三個select語句,第三個select查詢語句就是在事務C提交之后進行查詢的,那么此時的readview視圖如下,依舊不變,因為操作的是id為1的值,因此undolog版本鏈路上會多一條數據。
此時的頭節點head的事務id為104,min_id為103,max_id為104。根據版本對比規則,符合第二條,但是此時的head對應的事務id還在數組中,因此這個結點的數據并不可見;那么將繼續對比下一個結點,下一個結點的事務id為103,符合第二條,但是此時的head對應的事務id還在數組中,因此這個結點的數據也不可見;接下來對比第三條,head結點的事務id為102,小于min_id103,即事務id為102對應的結點時可見的,那么查詢到的值仍然時100
//第三個select查詢語句的值,由未提交的事務id數組和已創建的最大的事務id組成 [103,104],104因此不管后面有再多的其他事務更改,只要當前事務沒有提交,那么當前事務對應的readview就不會改變,通過undolog的日志版本鏈路,并且結合readview的版本比對規則,就可以找到一個可見的事務對應的數據,那并且這個值一定是最先獲取的值,就如上面商品的庫存,即使數據庫中的值真的變了,也可以通過這個mvcc機制來保證事務的隔離性,從而解決使用讀寫鎖效率低慢的問題。一句話總結就是:根據數據版本鏈對比規則,來讀取同一條數據在版本鏈上的不同版本數據,并且可以存在多個事務形成多個readview,但是版本鏈undolog只有一條
4,總結
mvcc被稱為多版本并發控制機制,由于mysql中的事務默認使用的是可重復讀,在這個隔離級別中并沒有解決幻讀問題,因此可以通過mvcc機制解決,并且還可以解決并發中讀寫鎖,讀寫沖突問題,從而提高并發讀寫的性能和效率。mvcc機制主要就是通過undolog的日志版本控制鏈和readview視圖組成。undolog鏈路中的每個結點由一個事務id和一個指針組成,事務id是在更新或者插入數據時會生成,指針是用來指向上一個版本,在執行完更新語句時就會將這個事務id加入到版本鏈路中;readview視圖由未提交的事務id數組和已創建的最大的事務id組成,并且在一個事務中,第一次select查詢就會生成一個readview視圖,并且在事務提交之前該事務的readview視圖不變。然后根據readview視圖比對規則,其規則就是將undolog鏈路中的頭節點為head結點,將數組中的最小id為min_id,將已創建的最大的id為max_id,然后根據視圖比對規則,找到一個事務id是可見的,那么找到的第一個可見的值,該事務id對應結點的值就是需要查詢出來的值。主要就是通過版本鏈比對規則,來讀取同一條數據版本鏈路上面的不同數據。這樣就可以保證在一個事務中查詢的值可以一直不變,不受其他事務的影響,并且這種方案的效率遠遠高于讀寫鎖。
總結
以上是生活随笔為你收集整理的【MySQL进阶-05】深入理解mvcc机制(详解)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云运维拓扑图_浅谈:智慧交通云平台网络拓
- 下一篇: 阻塞IO和非阻塞IO的区别 (BIONI