MySQL 加鎖處理分析(一)

1    背景    1html

1.1    MVCC:Snapshot Read vs Current Read    2mysql

1.2    Cluster Index:聚簇索引    3sql

1.3    2PL:Two-Phase Locking    3數據庫

1.4    Isolation Level    4併發

1.背景

MySQL/InnoDB的加鎖分析,一直是一個比較困難的話題。我在工做過程當中,常常會有同事諮詢這方面的問題。同時,微博上也常常會收到 MySQL鎖相關的私信,讓我幫助解決一些死鎖的問題。本文,準備就MySQL/InnoDB的加鎖問題,展開較爲深刻的分析與討論,主要是介紹一種思 路,運用此思路,拿到任何一條SQL語句,都能完整的分析出這條語句會加什麼鎖?會有什麼樣的使用風險?甚至是分析線上的一個死鎖場景,瞭解死鎖產生的原 因。性能

 

注:MySQL是一個支持插件式存儲引擎的數據庫系統。本文下面的全部介紹,都是基於InnoDB存儲引擎,其餘引擎的表現,會有較大的區別。spa

 

1.1  MVCC:Snapshot Read vs Current Read

 

MySQL InnoDB存儲引擎,實現的是基於多版本的併發控制協議——MVCC (Multi-Version Concurrency Control) (注:與MVCC相對的,是基於鎖的併發控制,Lock-Based Concurrency Control)。MVCC最大的好處,相信也是耳熟能詳:讀不加鎖,讀寫不衝突。在讀多寫少的OLTP應用中,讀寫不衝突是很是重要的,極大的增長了系 統的併發性能,這也是爲何現階段,幾乎全部的RDBMS,都支持了MVCC。.net

 

在MVCC併發控制中,讀操做能夠分紅兩類:快照讀 (snapshot read)與當前讀 (current read)。快照讀,讀取的是記錄的可見版本 (有多是歷史版本),不用加鎖。當前讀,讀取的是記錄的最新版本,而且,當前讀返回的記錄,都會加上鎖,保證其餘事務不會再併發修改這條記錄。插件

 

在一個支持MVCC併發控制的系統中,哪些讀操做是快照讀?哪些操做又是當前讀呢?以MySQL InnoDB爲例:htm

 

  • 快照讀:簡單的select操做,屬於快照讀,不加鎖。(固然,也有例外,下面會分析)

    • select * from table where ?;

     

  • 當前讀:特殊的讀操做,插入/更新/刪除操做,屬於當前讀,須要加鎖。

    • select * from table where ? lock in share mode;

    • select * from table where ? for update;

    • insert into table values (…);

    • update table set ? where ?;

    • delete from table where ?;

    全部以上的語句,都屬於當前讀,讀取記錄的最新版本。而且,讀取以後,還須要保證其餘併發事務不能修改當前記錄,對讀取記錄加鎖。其中,除了第一條語句,對讀取記錄加S鎖 (共享鎖)外,其餘的操做,都加的是X鎖 (排它鎖)。

     

爲何將 插入/更新/刪除 操做,都歸爲當前讀?能夠看看下面這個 更新 操做,在數據庫中的執行流程:

update 執行流程

從圖中,能夠看到,一個Update操做的具體流程。當Update SQL被髮給MySQL後,MySQL Server會根據where條件,讀取第一條知足條件的記錄,而後InnoDB引擎會將第一條記錄返回,並加鎖 (current read)。待MySQL Server收到這條加鎖的記錄以後,會再發起一個Update請求,更新這條記錄。一條記錄操做完成,再讀取下一條記錄,直至沒有知足條件的記錄爲止。 所以,Update操做內部,就包含了一個當前讀。同理,Delete操做也同樣。Insert操做會稍微有些不一樣,簡單來講,就是Insert操做可能 會觸發Unique Key的衝突檢查,也會進行一個當前讀。

 

根據上圖的交互,針對一條當前讀的SQL語句,InnoDB與MySQL Server的交互,是一條一條進行的,所以,加鎖也是一條一條進行的。先對一條知足條件的記錄加鎖,返回給MySQL Server,作一些DML操做;而後在讀取下一條加鎖,直至讀取完畢。

 

1.2  Cluster Index:聚簇索引

 

InnoDB存儲引擎的數據組織方式,是聚簇索引表:完整的記錄,存儲在主鍵索引中,經過主鍵索引,就能夠獲取記錄全部的列。關於聚簇索引表的組織方式,能夠參考MySQL的官方文檔:Clustered and Secondary Indexes 。本文假設讀者對這個,已經有了必定的認識,就再也不作具體的介紹。接下來的部分,主鍵索引/聚簇索引 兩個名稱,會有一些混用,望讀者知曉。

 

1.3  2PL:Two-Phase Locking

 

傳統RDBMS加鎖的一個原則,就是2PL (二階段鎖):Two-Phase Locking。相對而言,2PL比較容易理解,說的是鎖操做分爲兩個階段:加鎖階段與解鎖階段,而且保證加鎖階段與解鎖階段不相交。下面,仍舊以MySQL爲例,來簡單看看2PL在MySQL中的實現。

 

2PL

從上圖能夠看出,2PL就是將加鎖/解鎖分爲兩個徹底不相交的階段。加鎖階段:只加鎖,不放鎖。解鎖階段:只放鎖,不加鎖。

 

1.4  Isolation Level

 

隔離級別:Isolation Level, 也是RDBMS的一個關鍵特性。相信對數據庫有所瞭解的朋友,對於4種隔離級別:Read Uncommited,Read Committed,Repeatable Read,Serializable,都有了深刻的認識。本文不打算討論數據庫理論中,是如何定義這4種隔離級別的含義的,而是跟你們介紹一下 MySQL/InnoDB是如何定義這4種隔離級別的。

 

MySQL/InnoDB定義的4種隔離級別:

  • Read Uncommited

    能夠讀取未提交記錄。此隔離級別,不會使用,忽略。

  • Read Committed (RC)

    快照讀忽略,本文不考慮。

    針對當前讀,RC隔離級別保證對讀取到的記錄加鎖 (記錄鎖),存在幻讀現象。

  • Repeatable Read (RR)

    快照讀忽略,本文不考慮。

    針對當前讀,RR隔離級別保證對讀取到的記錄加鎖 (記錄鎖),同時保證對讀取的範圍加鎖,新的知足查詢條件的記錄不可以插入 (間隙鎖),不存在幻讀現象。

  • Serializable

    從MVCC併發控制退化爲基於鎖的併發控制。不區別快照讀與當前讀,全部的讀操做均爲當前讀,讀加讀鎖 (S鎖),寫加寫鎖 (X鎖)。

    Serializable隔離級別下,讀寫衝突,所以併發度急劇降低,在MySQL/InnoDB下不建議使用。

備註:下接  MySQL 加鎖處理分析(二)

相關文章
相關標籤/搜索