更新時間:2021-01-20 17:08:00 來源:動力節點 瀏覽1552次
MVCC(multiversion concurrency control),多版本并發控制,在MySQL數據庫中主要是通過在每一行記錄中增加三個字段,與undo log 中相關記錄配合使用,同時加上可見性算法,使得各個事務可以在不加鎖的情況下能夠同時地讀取到某行記錄上的準確值(這個值對不同的事務而言可能是不同的)。使用MVCC,在不加鎖的情況下也能讀取到準確的數據,大大提高了并發效率。本文我們就來講MySQL中的MVCC。
那么為什么需要MVCC呢?數據庫通常使用鎖來實現隔離性。最原生的鎖,鎖住一個資源后會禁止其他任何線程訪問同一個資源。但是很多應用的一個特點都是讀多寫少的場景,很多數據的讀取次數遠大于修改的次數,而讀取數據間互相排斥顯得不是很必要。所以就使用了一種讀寫鎖的方法,讀鎖和讀鎖之間不互斥,而寫鎖和寫鎖、讀鎖都互斥。這樣就很大提升了系統的并發能力。之后人們發現并發讀還是不夠,又提出了能不能讓讀寫之間也不沖突的方法,就是讀取數據時通過一種類似快照的方式將數據保存下來,這樣讀鎖就和寫鎖不沖突了,不同的事務session會看到自己特定版本的數據。當然快照是一種概念模型,不同的數據庫可能用不同的方式來實現這種功能。
舉個例子,程序員A正在讀數據庫中某些內容,而程序員B正在給這些內容做修改(假設是在一個事務內修改,大概持續10s左右),A在這10s內 則可能看到一個不一致的數據,在B沒有提交前,如何讓A能夠一直讀到的數據都是一致的呢?
有幾種處理方法,第一種: 基于鎖的并發控制,程序員B開始修改數據時,給這些數據加上鎖,程序員A這時再讀,就發現讀取不了,處于等待情況,只能等B操作完才能讀數據,這保證A不會讀到一個不一致的數據,但是這個會影響程序的運行效率。還有一種就是:MVCC,每個用戶連接數據庫時,看到的都是某一特定時刻的數據庫快照,在B的事務沒有提交之前,A始終讀到的是某一特定時刻的數據庫快照,不會讀到B事務中的數據修改情況,直到B事務提交,才會讀取B的修改內容。
一個支持MVCC的數據庫,在更新某些數據時,并非使用新數據覆蓋舊數據,而是標記舊數據是過時的,同時在其他地方新增一個數據版本。因此,同一份數據有多個版本存儲,但只有一個是最新的。
MVCC提供了時間一致性的處理思路,在MVCC下讀事務時,通常使用一個時間戳或者事務ID來確定訪問哪個狀態的數據庫及哪些版本的數據。讀事務跟寫事務彼此是隔離開來的,彼此之間不會影響。假設同一份數據,既有讀事務訪問,又有寫事務操作,實際上,寫事務會新建一個新的數據版本,而讀事務訪問的是舊的數據版本,直到寫事務提交,讀事務才會訪問到這個新的數據版本。
MVCC有兩種實現方式,第一種實現方式是將數據記錄的多個版本保存在數據庫中,當這些不同版本數據不再需要時,垃圾收集器回收這些記錄。這個方式被PostgreSQL和Firebird/Interbase采用,SQL Server使用的類似機制,所不同的是舊版本數據不是保存在數據庫中,而保存在不同于主數據庫的另外一個數據庫tempdb中。
第二種實現方式只在數據庫保存最新版本的數據,但是會在使用undo時動態重構舊版本數據,這種方式被Oracle和MySQL/InnoDB使用。
MVCC只在 READ COMMITTED 和 REPEATABLE READ 兩個隔離級別下工作。其他兩個隔離級別夠和MVCC不兼容, 因為 READ UNCOMMITTED 總是讀取最新的數據行, 而不是符合當前事務版本的數據行。而 SERIALIZABLE 則會對所有讀取的行都加鎖。如果你是可重復讀隔離級別REPEATABLE_READ,這時候你的ReadView還是第一次select時候生成的ReadView,也就是列表的值還是[100]。所以select的結果是強哥1。所以第二次select結果和第一次一樣,所以叫可重復讀!也就是說已提交讀隔離級別下的事務在每次查詢的開始都會生成一個獨立的ReadView,而可重復讀隔離級別則在第一次讀的時候生成一個ReadView,之后的讀都復用之前的ReadView。
以上就是MySQL中的MVCC,通過版本鏈,實現多版本,可并發讀-寫,寫-讀。通過ReadView生成策略的不同實現不同的隔離級別。這也是MySQL數據庫中非常重要的幾個機制之一,想要了解更多MySQL數據庫中的機制,請觀看本站的MySQL教程,名師為你講解最新最全的MySQL數據庫知識!
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習