1. 程式人生 > >MySQL5-資料庫鎖定機制

MySQL5-資料庫鎖定機制

MySQL資料庫鎖定機制

MySQL資料庫由於其自身架構的特點,存在多種資料儲存引擎,每種儲存引擎所針對的應用場景特點都不太一樣,為了滿足各自特定應用場景的需求,每種儲存引擎的鎖定機制都是為各自所面對的特定場景而優化設計,所以各儲存引擎的鎖定機制也有較大區別。

總的來說,MySQL各儲存引擎使用了三種類型(級別)的鎖定機制:行級鎖定,頁級鎖定和表級鎖定。

①行級鎖定(row-level)

行級鎖定最大的特點就是鎖定物件的顆粒度很小,也是目前各大資料庫管理軟體所實現的鎖定顆粒度最小的。由於鎖定顆粒度很小,所以發生鎖定資源爭用的概率也最小,能夠給予應用程式儘可能大的併發處理能力而提高一些需要高併發應用系統的整體效能。

但由於鎖定資源的顆粒度很小,所以每次獲取鎖和釋放鎖需要做的事情也更多,帶來的消耗自然也就更大了。此外,行級鎖定也最容易發生死鎖。

②表級鎖定(table-level)

表級別的鎖定是MySQL各儲存引擎中最大顆粒度的鎖定機制。該鎖定機制最大的特點是實現邏輯非常簡單,帶來的系統負面影響最小。所以獲取鎖和釋放鎖的速度很快。由於表級鎖一次會將整個表鎖定,所以可以很好的避免困擾我們的死鎖問題。

鎖定顆粒度大所帶來最大的負面影響就是出現鎖定資源爭用的概率也會最高,致使並大度大打折扣。

③頁級鎖定(page-level)

頁級鎖定是MySQL中比較獨特的一種鎖定級別。頁級鎖定的特點是鎖定顆粒度介於行級鎖定與表級鎖之間,所以獲取鎖定所需要的資源開銷,以及所能提供的併發處理能力也同樣是介於上面二者之間。另外頁級鎖定和行級鎖定一樣,會發生死鎖。

在資料庫實現資源鎖定的過程中,隨著鎖定資源顆粒度的減小,鎖定相同資料量的資料所需要消耗的記憶體數量是越來越多的,實現演算法也會越來越複雜。不過,隨著鎖定資源顆粒度的減小,應用程式的訪問請求遇到鎖等待的可能性也會隨之降低,系統整體併發度也隨之提升。

在MySQL資料庫中,使用表級鎖定的主要是MyISAM,Memory,CSV 等一些非事務性儲存引擎,而使用行級鎖定的主要是Innodb儲存引擎和NDB Cluster儲存引擎,頁級鎖定主要是BerkeleyDB儲存引擎的鎖定方式。

鎖歷史發展

小於MySQL3.23版本時,MySQL希望設計一種完全獨立於各種儲存引擎的鎖定機制,MySQL只有MyISAM和Momery儲存引擎,可以實現其當初設計思路。

大於等於MySQL3.23版本時,他們發現一個執行緒正在讀某個表的時候,另一個執行緒是可以對該表進行insert 操作的,只不過只能INSERT 到資料檔案的最尾部。這稱為Concurrent Insert。當出現Concurrent Insert 之後,MySQL 的開發人員不得不修改之前系統中的鎖定實現功能,但是僅僅只是增加了對Concurrent Insert 的支援,並沒有改動整體架構。可是在不久之後,隨著BerkeleyDB儲存引擎的引入,之前的鎖定機制遇到了更大的挑戰。因為BerkeleyDB儲存引擎並沒有MyISAM 和Memory儲存引擎同一時刻只允許單一執行緒訪問某一個表的限制,而是將這個單執行緒訪問限制的顆粒度縮小到了單個page,這又一次迫使MySQL 開發人員不得不再一次修改鎖定機制的實現。

由於新的儲存引擎的引入,導致鎖定機制不能滿足要求,讓MySQL 的人意識到已經不可能實現一種完全獨立的滿足各種儲存引擎要求的鎖定實現機制。如果因為鎖定機制的拙劣實現而導致儲存引擎的整體效能的下降,肯定會嚴重打擊儲存引擎提供者的積極性,這是MySQL 公司非常不願意看到的,因為這完全不符合MySQL 的戰略發展思路。所以工程師們不得不放棄了最初的設計初衷,在鎖定實現機制中作出修改,允許儲存引擎自己改變MySQL 通過介面傳入的鎖定型別而自行決定該怎樣鎖定資料。

總結:由於不同引擎來自於不同廠家,因此不同廠家有不同的實現原理,MySQL能做的就是一個介面,至於細節MySQL無法掌控,這有點類似於JDBC,我只管介面不管細節。

各種鎖定機制分析:表級鎖定

MySQL的表級鎖定主要分為兩種型別,一種是讀鎖定,另一種是寫鎖定。在MySQL中主要通過四個佇列來維護這兩種鎖定:兩個存放當前正在鎖定中的讀和寫鎖定資訊,另外兩個存放等待中的讀寫鎖定資訊:

Current read-lock queue (lock->read)

Pending read-lock queue (lock->read_wait)

Current write-lock queue (lock->write)

Pending write-lock queue (lock->write_wait)

當前持有讀鎖的所有執行緒的相關資訊都能夠在Current read-lock queue中找到,佇列中的資訊按照獲取到鎖的時間依序存放。而正在等待鎖定資源的資訊則存放在Pending read-lock queue裡面,另外兩個存放寫鎖資訊的佇列也按照上面相同規則來存放資訊。

雖然對於我們這些使用者來說MySQL展現出來的鎖定(表鎖定)只有讀鎖定和寫鎖定這兩種型別,但是在MySQL 內部實現中卻有多達11 種鎖定型別,由系統中一個列舉量(thr_lock_type)定義,具體詳情見附錄。

讀鎖定

一個新的客戶端請求在申請獲取讀鎖定資源的時候,需要滿足兩個條件:

1、請求鎖定的資源當前沒有被寫鎖定;

2、寫鎖定等待佇列(Pending write-lock queue)中沒有更高優先順序的寫鎖定等待;

如果滿足了上面兩個條件之後,該請求會被立即通過,並將相關的資訊存入Current read-lock queue中,而如果上面兩個條件中任何一個沒有滿足,都會被迫進入等待佇列Pending read-lock queue中等待資源的釋放。

寫鎖定

當客戶端請求寫鎖定的時候,MySQL首先檢查在Current write-lock queue是否已經有鎖定相同資源的資訊存在。如果存在了鎖定相同資源的寫鎖存在,那隻能進入等待佇列等待相應資源鎖定的釋放了。如果沒有,則再檢查Pending write-lock queue。

如果在Pending write-lock queue中找到了,自己也需要進入等待佇列並暫停自身執行緒等待鎖定資源。如果沒找到,則再檢測Current read-lock queue,如果有鎖定存在,則同樣需要進入Pending write-lock queue 等待。

當然,也可能遇到以下這兩種特殊情況:

①請求鎖定的型別為WRITE_DELAYED;

②請求鎖定的型別為WRITE_CONCURRENT_INSERT或者是TL_WRITE_ALLOW_WRITE且同時Current read lock是READ_NO_INSERT的鎖定型別。

當遇到這兩種特殊情況的時候,寫鎖定會立即獲得而進入Current write-lock queue 中。

各種鎖定機制分析:行級鎖定

考慮到行級鎖定君由各個儲存引擎自行實現,而且具體實現也各有差別,而Innodb 是目前事務型儲存引擎中使用最為廣泛的儲存引擎,所以這裡我們就主要分析一下Innodb 的鎖定特性。

Innodb 的行級鎖定同樣分為兩種型別,共享鎖和排他鎖,而在鎖定機制的實現過程中為了讓行級鎖定和表級鎖定共存, Innodb 也同樣使用了意向鎖(表級鎖定)的概念,也就有了意向共享鎖和意向排他鎖這兩種。

當一個事務需要給自己需要的某個資源加鎖的時候,如果遇到一個共享鎖正鎖定著自己需要的資源的時候,自己可以再加一個共享鎖,不過不能加排他鎖。但是,如果遇到自己需要鎖定的資源已經被一個排他鎖佔有之後,則只能等待該鎖定釋放資源之後自己才能獲取鎖定資源並新增自己的鎖定。而意向鎖的作用就是當一個事務在需要獲取資源鎖定的時候,如果遇到自己需要的資源已經被排他鎖佔用的時候,該事務可以需要鎖定行的表上面新增一個合適的意向鎖。如果自己需要一個共享鎖,那麼就在表上面新增一個意向共享鎖。而如果自己需要的是某行(或者某些行)上面新增一個排他鎖的話,則先在表上面新增一個意向排他鎖。意向共享鎖可以同時並存多個,但是意向排他鎖同時只能有一個存在。所以,可以說Innodb 的鎖定模式實際上可以分為四種:共享鎖(S),排他鎖(X),意向共享鎖(IS)和意向排他鎖(IX),我們可以通過以下表格來總結上面這四種所的共存邏輯關係:

 

共享鎖(S)

排他鎖(X)

意向共享鎖(IS)

意向排他鎖(IX)

共享鎖(S)

相容

衝突

相容

衝突

排他鎖(X)

衝突

衝突

衝突

衝突

意向共享鎖(IS)

相容

衝突

相容

相容

意向排他鎖(IX)

衝突

衝突

相容

相容

在Innodb 的事務管理和鎖定機制中,有專門檢測死鎖的機制,會在系統中產生死鎖之後的很短時間內就檢測到該死鎖的存在。當Innodb 檢測到系統中產生了死鎖之後,Innodb 會通過相應的判斷來選這產生死鎖的兩個事務中較小的事務來回滾,而讓另外一個較大的事務成功完成。實際上在Innodb 發現死鎖之後,會計算出兩個事務各自插入、更新或者刪除的資料量來判定兩個事務的大小。也就是說哪個事務所改變的記錄條數越多,在死鎖中就越不會被回滾掉。

thr_lock_type取值及定義

IGNORE

當發生鎖請求的時候內部互動使用,在鎖定結構和佇列中並不會有任何資訊儲存

UNLOCK

釋放鎖定請求的互動用所型別

READ

普通讀鎖定

WRITE

普通寫鎖定

READ_WITH_SHARED_LOCKS

在Innodb中使用到,由如下方式產生如:SELECT ... LOCK IN SHARE MODE

READ_HIGH_PRIORITY

高優先順序讀鎖定

READ_NO_INSERT

不允許Concurent Insert的鎖定

WRITE_ALLOW_WRITE

這個型別實際上就是當由儲存引擎自行處理鎖定的時候,mysqld 允許其他的執行緒再獲取讀或者寫鎖定,因為即使資源衝突,儲存引擎自己也會知道怎麼來處理

WRITE_ALLOW_READ

這種鎖定發生在對錶做DDL(ALTER TABLE ...)的時候,MySQL 可以允許其他執行緒獲取讀鎖定,因為MySQL 是通過重建整個表然後再RENAME 而實現的該功能,所在整個過程原表仍然可以提供讀服務

WRITE_CONCURRENT_INSERT

正在進行Concurent Insert 時候所使用的鎖定方式,該鎖定進行的時候,除了READ_NO_INSERT之外的其他任何讀鎖定請求都不會被阻塞

WRITE_DELAYED

在使用INSERT DELAYED 時候的鎖定型別

WRITE_LOW_PRIORITY

顯示宣告的低級別鎖定方式, 通過設定LOW_PRIORITY_UPDAT = 1 而產生

WRITE_ONLY

當在操作過程中某個鎖定異常中斷之後系統內部需要進行CLOSE TABLE 操作,在這個過程中出現的鎖定型別就是WRITE_ONLY。