Mysql加鎖過程詳解(4)-innodb 多版本併發控制原理詳解

多版本並控制技被普遍運用於各大數據,OracleMS SQL Server 2005+, Postgresql, Firebird, Maria等等,開源數據MYSQL中流行的INNODB引擎也採用了相似的並控制技.本文就將例來解析不一樣事隔離等INNODBMVCC實現原理.mysql

MVCC概述

1.1 MVCC簡介

MVCC (Multiversion Concurrency Control),即多版本併發控制技術,它使得大部分支持行鎖的事務引擎,再也不單純的使用行鎖來進行數據庫的併發控制,取而代之的是,把數據庫的行鎖與行的多個版本結合起來,只須要很小的開銷,就能夠實現非鎖定讀,從而大大提升數據庫系統的併發性能.sql

1.2 實現原理

MVCC能夠提供基於某個時間點的快照,使得對於事務看來,老是能夠提供與事務開始時刻相一致的數據,而無論這個事務執行的時間有多長.因此在不一樣的事務看來,同一時刻看到的相同行的數據多是不同的,即一個行可能有多個版本.是否聽起來難以想象呢?數據庫

原來,爲了實現mvcc, innodb對每一行都加上了兩個隱含的列,其中一列存儲行被更新的時間,另一列存儲行被刪除的時間. 可是innodb存儲的並非絕對的時間,而是與時間對應的數據庫系統的版本號,每當一個事務開始的時候,innodb都會給這個事務分配一個遞增的版本號,因此版本號也能夠被認爲是事務號.對於每個查詢語句,innodb都會把這個查詢語句的版本號同這個查詢語句遇到的行的版本號進行對比,而後結合不一樣的事務隔離等級,來決定是否返回該行.併發

下面分別以selectdelete、 insert、 update語句來講明:mvc

首先明確,事務越晚開始 ,其事務號越大。性能

1) SELECT大數據

對於select語句,只有同時知足了下面兩個條件的行,才能被返回:優化

行的被修改版本號小於或者等於該事務號spa

行的被刪除版本號要麼沒有被定義,要麼大於事務的版本號:行的刪除版本號若是沒有被定義,說明該行沒有被刪除過;若是刪除版本號大於當前事務的事務號,說明該行是被該事務後面啓動的事務刪除的,因爲是repeatable read隔離等級,後開始的事務對數據的影響不該該被先開始的事務看見,因此該行應該被返回.索引

2) INSERT

對新插入的行,行的更新版本被修改成該事務的事務號

3) DELETE

對於刪除,innodb直接把該行的被刪除版本號設置爲當前的事務號,至關於標記爲刪除,而不是實際刪除

4) UPDATE

在更新行的時候,innodb會把原來的行復制一份到回滾段中,並把當前的事務號做爲該行的更新版本

1.3 MVCC的優缺點

上述策略的結果就是,在讀取數據的時候,innodb幾乎不用得到任何鎖每一個查詢都經過版本檢查,只得到本身須要的數據版本,從而大大提升了系統的併發度.

這種策略的缺點是,爲了實現多版本,innodb必須對每行增長相應的字段來存儲版本信息,同時須要維護每一行的版本信息,並且在檢索行的時候,須要進行版本的比較,於是下降了查詢的效率;innodb還必須按期清理再也不須要的行版本,及時回收空間,這也增長了一些開銷

INNODB支持的事務隔離等級

INNODB支持並實現了ISO標準的4個事務隔離等級,即 READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.

1) READ UNCOMMITTED (能夠讀未提交的): 查詢能夠讀取到其餘事務正在修改的數據,即便其餘事務的修改尚未提交.這種隔離等級沒法避免髒讀.

2) READ COMMITTED(只能夠讀已經提交的):其餘事務對數據庫的修改,只要已經提交,其修改的結果就是可見的,與這兩個事務開始的前後順序無關.這種隔離等級避免了髒讀,可是沒法實現可重複讀,甚至有可能產生幻讀.

3) REPEATABLE READ(可重複讀):read committed更進了一步,它只能讀取在它開始以前已經提交的事務對數據庫的修改,在它開始之後,全部其餘事務對數據庫的修改對它來講均不可見.從而實現了可重複讀,可是仍有可能幻讀

4) SERIALIZABLE(可串行化):這是事務隔離等級的最高級別.其實現原理就是對於全部的query,即便是查詢,也會加上讀鎖,避免其餘事務對數據的修改.因此它成功的避免了幻讀.可是代價是,數據庫系統的併發處理能力大大下降,因此它不會被用到生產系統中.

咱們對MVCC和標準事務隔離等級有所瞭解之後,再結合實例來看看其具體表現吧.

3 不一樣事務隔離等級下的MVCC實現

MVCC因爲其實現原理,只支持read committedrepeatable read隔離等級,

在不一樣的事務中,即便執行相同的語句,獲得的結果也可能不同,開啓事務的時間不一樣,獲得的結果也可能不一樣,由於每行數據都存在多版本的記錄,事務老是能夠看到自身對數據庫數據的修改,當全部的事務都提交以後,它們看到的結果是一致的。這就是由MVCC控制的。MVCC就是經過事務發生的不一樣的時間點,與數據行的版原本進行對比,從而取回與事務開始的時間點相一致的數據,來實現非阻塞的一致讀。

相關的Q&A

4.1 爲何select count(*)myisam表上很快,而在Innodb的表上很慢?

由於innodb採用了MVCC技術,對於相同的行,可能同時存在多個版本,innodb必須根據查詢的時間來過濾掉一些行,才能得出結果,必然要執行全表掃描,而全表掃描是很是耗時的.對於myisam的表,任何行都只有一個版本,mysql甚至不須要掃描就能夠直接返回精確的統計結果,咱們用explain也能夠看到,對於myisam的表,執行select count(*)的時候,mysql顯示」 Select tables optimized away」,查詢直接被優化了;而對於innodb的表,多是全表掃描,也多是using index」,總之,速度確定會比myisam的錶慢不少.

4.2 個人數據庫只是頻繁更新,沒有插入新數據,可是爲何表空間佔用會愈來愈大?

若是你在數據庫中執行了大事務, innodb就會把被修改數據的前映像存放到稱爲回滾段的公共表空間中,並且對於索引和表中的行的多個版本,若是innodb來不及purge,或者這些行由於要提供一致讀而不能被purge,就會佔用愈來愈多的空間,甚至有可能短期撐爆你的硬盤.因此應用程序中須要合理控制事務的大小。

4.3 能禁用MVCC?

禁用MVCC能夠下降innodb引擎的開銷,而同時innodb又能夠支持外鍵約束,能夠實現自動恢復.MVCC自己不支持read uncommitted等級,因此能夠經過設置transaction_isolation = read uncommitted 來禁用MVCC.可是任何改變innodb默認隔離等級的操做,都會起到innodb_locks_unsafe_for_binlog=off相似的效果,這會致使諸如insert into t select * from t_src 之類的語句再也不給源表t_src加鎖,也再也不使用innodb的間隙鎖,從而產生幻讀,直接致使binlog中記錄的sql語句不能正確的串行化,從而主從數據庫的數據再也不一致,並且基於binlog的增量備份也再也不有效.因此除非不須要記錄binlog,不然別這麼作.固然咱們能夠這樣作來優化從庫的性能,由於從庫不須要記錄binlog.

4.4 什麼時候使用char類型,什麼時候使用varchar類型的列?

在使用myisam引擎的狀況下,定長表雖然可能佔用較多的存儲空間,可是它會加快檢索和全表掃描的速度,此時適合選用char的列,而對於表中的變長的列,能夠採用分表的方法把變長的列拆分出去,提升定長表的檢索性能.而若是使用的是innodb的引擎,因爲innodbmvcc策略的實施,char數據類型相對於varchar類型幾乎沒有任何優點,反而varchar列可能節省更多的存儲空間,建議使用varchar數據類型.

相關文章
相關標籤/搜索