mysql四種隔離級別和mvcc以及鎖之間的關係

什麼是事務的隔離級別

隔離級別:理解隔離級別之前需要先了解事務的四個特性,分別是:

A(Atomic):原子性是指數據庫的事務是一個不可分割的工作單位,只有數據庫事務都成功纔算成功,任何一個SQL的失敗,數據庫狀態都必須回退到事務開始前的狀態 
C(Consistence):一致性是指事務將數據庫的狀態從一種狀態轉變爲下一個一致的狀態。在事務的開始和結束後,數據庫的完整性約束都沒有被破壞。 
I(Isolation):隔離性是指事務之間對數據對象的讀寫是相互隔離的,具體是提交後可見還是提交後也不可見取決於隔離等級,前面兩種情況分別爲提交讀(RC)和可重複讀(RR)。 
D(持久性):持久性是指事務一旦提交,其修改是永久性的,即使還未寫入磁盤時發生宕機,也能恢復數據。 

由上面對事務隔離性的描述可以知道,事務的隔離級別主要是用來控制多事務併發時的數據可見性,即在一個事務開始後,能否看到另一個事務更新或插入的數據。

事務的隔離級別有四種:

READ-UNCOMMITTED(未提交讀):一個事務可以讀取到另一個事務未提交的數據,如果事務回滾時,會發生髒讀(對錯誤數據進行了處理)

READ-COMMITTED(提交讀):不會讀到另一個事務未提交事務的數據,但是如果事務提交以後,再次查詢,兩次查詢的數據不一致(在一個事務中,兩次查詢的數據不一致),也叫不可重複讀。

REPEATABLE-READ(可重複讀):同一個事務中,不論另一個事務是否刪除、更新、插入,本次查詢的數據一致(快照讀,相當於給數據拍了一張照片),也叫可重複讀,可重複讀的實現就是通過mvcc和鎖來實現的。

SERIALIZABLE(序列化):是最高的事務隔離級別,在該級別下,事務串行化順序執行(鎖表,只有當一個事務處理完成以後,另一個事務才能處理數據),可以避免髒讀、不可重複讀與幻讀。但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。

什麼是mvcc

在講述事務隔離級別的時候已經帶出了mvcc了, mvcc是一種多版本併發控制機制,它使得大部分支持行鎖的事務引擎,不再單純的使用行鎖來進行數據庫的併發控制,取而代之的是把數據庫的行鎖與行的多個版本結合起來,只需要很小的開銷,就可以實現非鎖定讀,從而大大提高數據庫系統的併發性能。

MVCC主要是爲Repeatable-Read事務隔離級別做的。在此隔離級別下,A、B事務所示的數據相互隔離,互相更新不可見。

我們可以通過InnoDB的MVCC實現來分析MVCC使怎樣進行併發控制的. 

innodb存儲的最基本row中包含一些額外的存儲信息 DATA_TRX_ID,DATA_ROLL_PTR,DB_ROW_ID,DELETE BIT

  • 6字節的DATA_TRX_ID 標記了最新更新這條行記錄的transaction id,每處理一個事務,其值自動+1

  • 7字節的DATA_ROLL_PTR 指向當前記錄項的rollback segment的undo log記錄,找之前版本的數據就是通過這個指針

  • 6字節的DB_ROW_ID,當由innodb自動產生聚集索引時,聚集索引包括這個DB_ROW_ID的值,否則聚集索引中不包括這個值.,這個用於索引當中
  • DELETE BIT位用於標識該記錄是否被刪除,這裏的不是真正的刪除數據,而是標誌出來的刪除。真正意義的刪除是在commit的時候

具體的執行過程

begin->用排他鎖鎖定該行->記錄redo log->記錄undo log->修改當前行的值,寫事務編號,回滾指針指向undo log中的修改前的行

上述過程確切地說是描述了UPDATE的事務過程,其實undo log分insert和update undo log,因爲insert時,原始的數據並不存在,所以回滾時把insert undo log丟棄即可,而update undo log則必須遵守上述過程

下面分別以select、delete、 insert、 update語句來說明

SELECT

Innodb檢查每行數據,確保他們符合兩個標準:

1、InnoDB只查找版本早於當前事務版本的數據行(也就是數據行的版本必須小於等於事務的版本),這確保當前事務讀取的行都是事務之前已經存在的,或者是由當前事務創建或修改的行

2、行的刪除操作的版本一定是未定義的或者大於當前事務的版本號,確定了當前事務開始之前,行沒有被刪除

符合了以上兩點則返回查詢結果。

INSERT

InnoDB爲每個新增行記錄當前系統版本號作爲創建ID。

DELETE

InnoDB爲每個刪除行的記錄當前系統版本號作爲行的刪除ID。

UPDATE

InnoDB複製了一行。這個新行的版本號使用了系統版本號。它也把系統版本號作爲了刪除行的版本。

說明

insert操作時 “創建時間”=DB_ROW_ID,這時,“刪除時間 ”是未定義的;

update時,複製新增行的“創建時間”=DB_ROW_ID,刪除時間未定義,舊數據行“創建時間”不變,刪除時間=該事務的DB_ROW_ID;

delete操作,相應數據行的“創建時間”不變,刪除時間=該事務的DB_ROW_ID;

select操作對兩者都不修改,只讀相應的數據

在mysql中什麼是鎖

mysql中鎖的概念有很多,樂觀鎖、悲觀鎖、共享鎖、排他鎖、表鎖、行鎖等,他們之間有什麼關係呢

樂觀鎖

樂觀鎖大多是基於數據版本記錄機制實現,一般是給數據庫表增加一個"version"字段。讀取數據時,將此版本號一同讀出,之後更新時,對此版本號加一。此時將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大於數據庫表當前版本號,則予以更新,否則認爲是過期數據。

悲觀鎖

悲觀鎖依靠數據庫提供的鎖機制實現。MySQL中的共享鎖和排它鎖都是悲觀鎖。數據庫的增刪改操作默認都會加排他鎖,而查詢不會加任何鎖。

共享鎖(讀鎖)

共享鎖指的就是對於多個不同的事務,對於一個資源共享同一個鎖。對某一資源加共享鎖,自身可可讀該資源,其他人也可以讀該資源(也可以再加共享鎖,即共享鎖共享多個內存),但無法修改。要想修改就必須等所有共享鎖都釋放完之後。語法:select * from table lock in share mode;

排它鎖(寫鎖)

排它鎖指的就是對於多個不同的事務,對同一個資源只能有一把鎖。對某一資源加排它鎖,自身可以進行增刪改查,其他人無法進行加鎖操作,更無法進行增刪改操作。語法:select * from table for update

行鎖

行鎖就是給一行數據進行加鎖,操作對象是數據表中的一行(共享鎖和排他鎖可能是行鎖也可能是表鎖,取決於對數據加鎖的範圍,是一行還是整個表)。是MVCC技術用的比較多的,但在MYISAM用不了,行級鎖用mysql的儲存引擎實現而不是mysql服務器。但行級鎖對系統開銷較大,處理高併發較好。

表鎖

表鎖就是對一張表進行加鎖,操作對象是數據表。Mysql大多數鎖策略都支持(常見mysql innodb),是系統開銷最低但併發性最低的一個鎖策略。事務t對整個表加讀鎖,則其他事務可讀不可寫,若加寫鎖,則其他事務增刪改都不行。

PS

當前讀、快照讀,record lock(記錄鎖)、gap lock(間隙鎖)、next-key lock

本來只有串讀隔離級別纔可以解決幻讀問題,而實際上由於快照讀的特性使可重複讀也解決了幻讀問題。

當前讀是因爲innodb默認爲它加入了間隙鎖,防止在事務期間對相關數據集插入記錄,從而避免出現幻讀。

在RR級別下,快照讀是通過MVVC(多版本控制)和undo log來實現的,當前讀是通過加record lock(記錄鎖)和gap lock(間隙鎖)來實現的。如果需要實時顯示數據,還是需要通過手動加鎖來實現。這個時候會使用next-key技術來實現。

在mysql中,提供了兩種事務隔離技術,第一個是mvcc,第二個是next-key技術。這個在使用不同的語句的時候可以動態選擇。不加lock inshare mode之類的快照讀就使用mvcc。否則 當前讀使用next-key。mvcc的優勢是不加鎖,併發性高。缺點是不是實時數據。next-key的優勢是獲取實時數據,但是需要加鎖。

Record lock
單條索引記錄上加鎖,record lock鎖住的永遠是索引,而非記錄本身,即使該表上沒有任何索引,那麼innodb會在後臺創建一個隱藏的聚集主鍵索引,那麼鎖住的就是這個隱藏的聚集主鍵索引。所以說當一條sql沒有走任何索引時,那麼將會在每一條聚集索引後面加X鎖,這個類似於表鎖,但原理上和表鎖應該是完全不同的。

Gap Lock 

在索引記錄之間的間隙中加鎖,或者是在某一條索引記錄之前或者之後加鎖,並不包括該索引記錄本身。gap lock的機制主要是解決可重複讀模式下的幻讀問題。

Next-Key Lock 

行鎖和間隙鎖組合起來就叫Next-Key Lock。

參考資料:https://blog.csdn.net/whoamiyang/article/details/51901888

                  https://www.cnblogs.com/chenpingzhao/p/5065316.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章