91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL多版本并發控制MVCC實例分析

發布時間:2022-07-26 09:32:58 來源:億速云 閱讀:116 作者:iii 欄目:開發技術

本篇內容介紹了“MySQL多版本并發控制MVCC實例分析”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

    1.什么是MVCC

    MVCC (Multiversion Concurrency Control),多版本并發控制。顧名思義,MVCC是通過數據行的多個版本管理來實現數據庫的并發控制。這項技術使得在InnoDB的事務隔離級別下執行一致性讀.操作有了保證。換言之,就是為了查詢一些正在被另一個事務更新的行,并且可以看到它們被更新之前的值,這樣在做查詢的時候就不用等待另一個事務釋放鎖。

    MVCC沒有正式的標準,在不同的DBMS中MVCC的實現方式可能是不同的,也不是普遍使用的(大家可以參考相關的DBMS文檔)。這里講解InnoDB中 MVCC的實現機制(MySQL其它的存儲引擎并不支持它)

    2快照讀與當前讀

    MVCC在MySQL InnoDB中的實現主要是為了提高數據庫并發性能,用更好的方式去處理讀-寫沖突,做到即使有讀寫沖突時,也能做到不加鎖非阻塞并發讀,而這個讀指的就是快照讀,而非當前讀。當前讀實際上是一種加鎖的操作,是悲觀鎖的實現。而MVCC本質是采用樂觀鎖思想的一種方式。

    2.1 快照讀

    快照讀又叫一致性讀,讀取的是快照數據。不加鎖的簡單的SELECT都屬于快照讀即不加鎖的非阻塞讀;比如這樣:

    select * from player where ...

    之所以出現快照讀的情況,是基于提高并發性能的考慮,快照讀的實現是基于MVCC,它在很多情況下,避免了加鎖操作,降低了開銷。

    既然是基于多版本,那么快照讀可能讀到的并不一定是數據的最新版本,而有可能是之前的歷史版本。

    快照讀的前提是隔離級別不是串行級別,串行級別下的快照讀會退化成當前讀。

    2.2當前讀

    當前讀讀取的是記錄的最新版本(最新數據,而不是歷史版本的數據),讀取時還要保證其他并發事務不能修改當前記錄,會對讀取的記錄進行加鎖。加鎖的SELECT,或者對數據進行增刪改都會進行當前讀。比如:

    MySQL多版本并發控制MVCC實例分析

    3.復習

    3.1 再談隔離級別

    我們知道事務有4個隔離級別,可能存在三種并發問題:

    MySQL多版本并發控制MVCC實例分析

    在MysQL 中,默認的隔離級別是可重復讀,可以解決臟讀和不可重復讀的問題,如果僅從定義的角度來看,它并不能解決幻讀問題。如果我們想要解決幻讀問題,就需要采用串行化的方式,也就是將隔離級別提升到最高,但這樣一來就會大幅降低數據庫的事務并發能力。

    MVCC可以不采用鎖機制,而是通過樂觀鎖的方式來解決不可重復讀和幻讀問題!它可以在大多數情況下替代行級鎖,降低系統的開銷。

    MySQL多版本并發控制MVCC實例分析

    3.2 隱藏字段、Undo Log版本鏈

    回顧一下undo日志的版本鏈,對于使用InnoDB存儲引擎的表來說,它的聚簇索引記錄中都包含兩個必要的隱藏列。

    trx_id:每次一個事務對某條聚簇索引記錄進行改動時,都會把該事務的事務id賦值給trx_id隱藏列。roll_pointer:每次對某條聚簇索引記錄進行改動時,都會把舊的版本寫入到undo日志中,然后這個隱藏列就相當于一個指針,可以通過它來找到該記錄修改前的信息。

    MySQL多版本并發控制MVCC實例分析

    MySQL多版本并發控制MVCC實例分析

    MySQL多版本并發控制MVCC實例分析

    MySQL多版本并發控制MVCC實例分析

    4、MVCC實現原理之ReadView

    MVCC的實現依賴于:隱藏字段、Undo Log、Read View

    4.1什么是ReadView

    在MVCC機制中,多個事務對同一個行記錄進行更新會產生多個歷史快照,這些歷史快照保存在Undo Log里。如果一個事務想要查詢這個行記錄,需要讀取哪個版本的行記錄呢?這時就需要用到ReadView了,它幫我們解決了行的可見性問題。

    ReadView就是事務在使用MVCC機制進行快照讀操作時產生的讀視圖。當事務啟動時,會生成數據庫系統當前的一個快照,InnoDB為每個事務構造了一個數組,用來記錄并維護系統當前活躍事務的lD(“"活躍"指的就是,啟動了但還沒提交)。

    4.2 設計思路

    使用READ UNCOMMITTED隔離級別的事務,由于可以讀到未提交事務修改過的記錄,所以直接讀取記錄的最新版本就好了。

    使用SERIALIZABLE隔離級別的事務,InnoDB規定使用加鎖的方式來訪問記錄。

    使用READ COMMITTEDREPEATABLE READ隔離級別的事務,都必須保證讀到已經提交了的事務修改過的記錄。假如另一個事務已經修改了記錄但是尚未提交,是不能直接讀取最新版本的記錄的,核心問題就是需要判斷一下版本鏈中的哪個版本是當前事務可見的,這是ReadView要解決的主要問題。

    這個ReadView中主要包含4個比較重要的內容,分別如下:

    MySQL多版本并發控制MVCC實例分析

    4.3 ReadView的規則

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

    • 如果被訪問版本的trx_id屬性值與ReadView中的creator_trx_id值相同,意味著當前事務在訪問它自己修改過的記錄,所以該版本可以被當前事務訪問。

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

    • 如果被訪問版本的trx_id屬性值大于或等于ReadView中的low_limit_id值,表明生成該版本的事務在當前事務生成ReadView后才開啟,所以該版本不可以被當前事務訪問。

    • 如果被訪問版本的trx_id屬性值在ReadView的up_limit_idlow_limit_id之間,那就需要判斷一下trx_id屬性值是不是在trx_ids列表中。如果在,說明創建ReadView時生成該版本的事務還是活躍的,該版本不可以被訪問。如果不在,說明創建ReadView時生成該版本的事務已經被提交,該版本可以被訪問。 4.4 MVCC整體操作流程

    了解了這些概念之后,我們來看下當查詢一條記錄的時候,系統如何通過MVCC找到它:

    • 1.首先獲取事務自己的版本號,也就是事務ID;

    • 2.獲取ReadView;

    • 3.查詢得到的數據,然后與ReadView中的事務版本號進行比較;

    • 4.如果不符合Readview規則,就需要從Undo Log中獲取歷史快照;

    • 5.最后返回符合規則的數據。

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

    InnoDB中,MVCC是通過Undo Log + Read View進行數據讀取,Undo Log保存了歷史快照,而Read View規則幫我們判斷當前版本的數據是否可見。

    在隔離級別為讀已提交(Read Committed)時,一個事務中的每一次select查詢都會重新獲取一次Read View。

    MySQL多版本并發控制MVCC實例分析

    當隔離級別為可重讀的時候,就避免了不可重復讀,這是因為一個事務只在第一次select的時候會獲取一次Read View,而后面所有的select都會復用這個Read View,

    如下所示:

    MySQL多版本并發控制MVCC實例分析

    5.舉例說明

    假設現在student表中只有一條由事務id8事務插入一條記錄:

    MySQL多版本并發控制MVCC實例分析

    MVCC只能在READ COMMITTED和REPEATABLE READ兩個隔離級別下工作。接下來看一下READ COMMITTEDREPEATABLE READ所謂的生成Readview的時機不同到底不同在哪里。

    5.1 READ COMMITTED

    隔離級別下:

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

    現在有兩個事務id分別為10、20的事務在執行:

    MySQL多版本并發控制MVCC實例分析

    說明:事務執行過程中,只有在第一次真正修改記錄時(比如使用INSERT、DELETE、UPDATE語句),才會被分配一個單獨的事務id,這個事務id是遞增的。所以我們才在事務2中更新一些別的表的記錄,目的是讓它分配事務id。

    此刻,表student中id為1的記錄得到的版本鏈表如下所示:

    MySQL多版本并發控制MVCC實例分析

    假設現在有一個使用READ COMMITED隔離級別的事務開始執行:

    MySQL多版本并發控制MVCC實例分析

    這個SELECT1的執行過程如下:

    步驟1∶在執行SELECT語句時會先生成一個ReadView ,ReadView的trx_ids列表的內容就是[10,20],up_limit_id10, low_limit_id21 , creator_trx_id0
    步驟2:從版本鏈中挑選可見的記錄,從圖中看出,最新版本的列name的內容是'王五',該版本的trx_id值為10,在trx_ids列表內,所以不符合可見性要求,根據roll_pointer跳到下一個版本。
    步驟3:下一個版本的列name的內容是'李四',該版本的trx_id值也為10,也在trx_ids列表內,所以也不符合要求,繼續跳到下一個版本。
    步驟4:下一個版本的列 name的內容是‘張三',該版本的trx_id值為8,小于ReadView 中的up_limit_id值10,所以這個版本是符合要求的,最后返回給用戶的版本就是這條列name為‘張三’的記錄。

    之后,我們把事務id10的事務提交一下:

    MySQL多版本并發控制MVCC實例分析

    然后再到事務id20的事務中更新一下表studentid1的記錄:

    MySQL多版本并發控制MVCC實例分析

    此刻,表student中id為1的記錄的版本鏈就長這樣:

    MySQL多版本并發控制MVCC實例分析

    然后再到剛才使用READ COMMITTED隔離級別的事務中繼續查找id為1的記錄,如下:

    MySQL多版本并發控制MVCC實例分析

    這個SELECT2的執行過程如下:

    步驟1∶在執行SELECT語句時會又會單獨生成一個ReadView,該ReadView的trx_ids列表的內容就是[20],up_limit_id為20,low_limit_id為21, creator_trx_id為0。
    步驟2:從版本鏈中挑選可見的記錄,從圖中看出,最新版本的列name的內容是‘宋八’,該版本的tr×_id值為20,在trx_ids列表內,所以不符合可見性要求,根據roll.pointer跳到下一個版本。
    步驟3∶下一個版本的列name的內容是’錢七’,該版本的trx_id值為20,也在trx_ids列表內,所以也不符合要求,繼續跳到下一個版本。
    步驟4∶下一個版本的列name的內容是’王五’,該版本的trx_id值為10,小于ReadView中的up_limit.id值20,所以這個版本是符合要求的,最后返回給用戶的版本就是這條列name為’王五’的記錄。
    以此類推,如果之后事務id為20的記錄也提交了,再次在使用READ CONMMITTED隔離級別的事務中查詢表student中id值為1的記錄時,得到的結果就是‘宋八’了,具體流程我們就不分析了。

    5.2 REPEATABLE READ

    隔離級別下:

    使用REPEATABLE READ隔離級別的事務來說,只會在第一次執行查詢語句時生成一個ReadView,之后的查詢就不會重復生成了。

    比如,系統里有兩個事務id分別為10、20的事務在執行:

    MySQL多版本并發控制MVCC實例分析

    此刻,表student中id為1的記錄得到的版本鏈表如下所示:

    MySQL多版本并發控制MVCC實例分析

    假設現在有一個使用REPEATABLE READ隔離級別的事務開始執行:

    MySQL多版本并發控制MVCC實例分析

    此時執行過程與read committed相同

    MySQL多版本并發控制MVCC實例分析

    MySQL多版本并發控制MVCC實例分析

    然后再到剛才使用REPEATABLE READ隔離級別的事務中繼續查找id為1的記錄,如下:

    MySQL多版本并發控制MVCC實例分析

    這個SELECT2的執行過程如下:

    步驟1:因為當前事務的隔離級別為REPEATABLE READ,而之前在執行SELECT1時已經生成過ReadView了,所以此時直接復用之前的ReadView,之前的ReadView的trx_ids列表的內容就是[10,20],up_limit_id為10, low_limit_id為21 , creator_trx_id為0。
    步驟2:然后從版本鏈中挑選可見的記錄,從圖中可以看出,最新版本的列name的內容是’宋八’trx_id值為20,在trx_ids列表內,所以不符合可見性要求,根據roll_pointer跳到下一個版本。
    步驟3:下一個版本的列name的內容是’錢七’,該版本的trx_id值為20,也在trx_ids列表內合要求,繼續跳到下一個版本。
    步驟4:下一個版本的列name的內容是’王五’,該版本的trx_id值為10,而trx_ids列表中是包含值為10的事務id的,所以該版本也不符合要求,同理下一個列name的內容是’李四’的版本也不符合要求。繼續跳到下個版本。
    步聚5∶下一個版本的列name的內容是’張三’,該版本的trx_id值為80,小于Readview中的up_limit_id值10,所以這個版本是符合要求的,最后返回給用戶的版本就是這條列c為‘張三’的記錄。
    兩次SELECT查詢得到的結果是重復的,記錄的列c值都是’張三’,這就是可重復讀的含義。如果我們之后再把事務id為20的記錄提交了,然后再到剛才使用REPEATABLE READ隔離級刷的事務中繼續查找這個id為1的記錄,得到的結果還是’張三’,具體執行過程大家可以自己分析一下。

    5.3 如何解決幻讀

    假設現在表student中只有一條數據,數據內容中,主鍵id=1,隱藏的trx_id=10,它的undo log如下圖所示。

    MySQL多版本并發控制MVCC實例分析

    假設現在有事務A和事務B并發執行,事務A的事務id為20,事務B的事務id為30。
    步驟1:事務A開始第一次查詢數據,查詢的SQL語句如下。

    select * from student where id > 1;

    在開始查詢之前,MySQL會為事務A產生一個ReadView,此時ReadView的內容如下: trx_ids=[20, 30 ] ,up_limit_id=20 , low_limit_id=31 , creator_trx_id=20。

    由于此時表student中只有一條數據,且符合where id>=1條件,因此會查詢出來。然后根據ReadView機制,發現該行數據的trx_id=10,小于事務A的ReadView里up_limit_id,這表示這條數據是事務A開啟之前,其他事務就已經提交了的數據,因此事務A可以讀取到。

    結論:事務A的第一次查詢,能讀取到一條數據,id=1。

    步驟2∶接著事務B(trx_id=30),往表student中新插入兩條數據,并提交事務。

    insert into student(id,name) values(2,'李四');
    insert into student(id,name) values(3,'王五');

    此時表student中就有三條數據了,對應的undo如下圖所示:

    MySQL多版本并發控制MVCC實例分析

    步驟3∶接著事務A開啟第二次查詢,根據可重復讀隔離級別的規則,此時事務A并不會再重新生成ReadView。此時表student中的3條數據都滿足 where id>=1的條件,因此會先查出來。然后根據ReadView機制,判斷每條數據是不是都可以被事務A看到。
    1)首先 id=1的這條數據,前面已經說過了,可以被事務A看到。
    2)然后是id=2的數據,它的trx_id=30,此時事務A發現,這個值處于up_limit_id和low_limit_id之間,因此還需要再判斷30是否處于trx_ids數組內。由于事務A的trx_ids=[20,30],因此在數組內,這表示id=2的這條數據是與事務A在同一時刻啟動的其他事務提交的,所以這條數據不能讓事務A看到。
    3)同理,id=3的這條數據, trx_id 也為30,因此也不能被事務A看見。

    MySQL多版本并發控制MVCC實例分析

    結論:最終事務A的第二次查詢,只能查詢出id=1的這條數據。這和事務A的第一次查詢的結果是一樣的,因此沒有出現幻讀現象,所以說在 MySQL的可重復續隔離級別下,不存在幻讀問題。

    “MySQL多版本并發控制MVCC實例分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    望奎县| 保靖县| 义马市| 桓仁| 天镇县| 外汇| 澄江县| 遂昌县| 镇沅| 金华市| 平利县| 泰州市| 阳东县| 额济纳旗| 孝感市| 丰都县| 富宁县| 新营市| 财经| 麦盖提县| 静安区| 敖汉旗| 逊克县| 盖州市| 巴彦县| 河东区| 噶尔县| 来宾市| 凤阳县| 五家渠市| 青川县| 长泰县| 峨边| 九龙县| 清水河县| 黔西| 逊克县| 咸丰县| 嘉义市| 同江市| 桐城市|