概述

資料庫鎖定機制簡單的來說,就是資料庫為了保證數據的一致性與完整性,而使各種共享資源在被並發訪問時變得有序所設計的一種規則。對於任何一種資料庫來說都需要有相應的鎖機制,所以MySQL也不能例外。MySQL資料庫根據鎖鎖定數據的顆粒度可分為表級鎖、行級鎖和頁級鎖。

一、表級鎖

1.1 什麼是表級鎖

表級鎖會直接鎖定整張表。表級鎖是MySQL各存儲引擎中最大顆粒度的鎖定機制。該鎖定機制最大的特點是實現邏輯簡單,帶來的系統負面影響最小。所以獲取鎖和釋放鎖的速度很快。由於表級鎖一次會將整個表鎖定,所以可以很好的避免死鎖問題。當然,鎖定顆粒度大所帶來負面影響就是出現爭用鎖定資源的概率也會最高,致使並發度大大降低。

1.2 表級鎖的鎖模式

MySQL的表級鎖有兩種模式:表共享讀鎖和表獨佔寫鎖;表共享讀鎖,不會阻塞其他用戶對同一表的讀請求,但會阻塞對同一表的寫請求;表獨佔寫鎖,會阻塞其他用戶對同一表的讀和寫操作;表共享讀鎖與表獨佔寫鎖之間,以及表獨佔寫鎖之間是串列的。當一個線程獲得對一個表的寫鎖後,只有持有鎖的線程可以對錶進行更新操作。其他線程的讀、寫操作都會等待,直到鎖被釋放為止。

1.3 表鎖優化建議

雖然使用表級鎖定在鎖定實現的過程中比實現行級鎖定或者頁級鎖所帶來的附加成本都要小,鎖定本身所消耗的資源也最少。但是由於鎖定的顆粒度比較大,所以造成爭用鎖定資源的情況也會比其他的鎖級別都要多,從而在較大程度上會降低並發處理能力。所以,在優化表級鎖問題的時候,最關鍵的就是如何讓其提高並發度。由於鎖定級別是不可能改變的了,所以我們首先需要儘可能讓鎖定的時間變短,然後就是讓可能並發進行的操作儘可能的並發。

二、行級鎖

2.1 什麼是行級鎖

行級鎖僅對指定的記錄加鎖。行級鎖最大的特點就是鎖定對象的顆粒度小,也是目前各大資料庫管理軟體所實現的鎖定顆粒度最小的。由於鎖定顆粒度很小,所以發生爭用鎖定資源的概率也最小,能儘可能大的提高資料庫的並發處理能力從而提高高並發應用系統的性能。雖然行級鎖能夠極大提高並發處理能力,但是由於鎖定資源的顆粒度很小,所以每次獲取鎖和釋放鎖需要做的事情也更多,帶來的消耗自然也就更大了。此外,行級鎖定也最容易發生死鎖。

2.2 行級鎖的鎖模式

MySQL行級鎖有共享鎖和排他鎖兩種。當一個事務需要給自己需要的某個資源加鎖的時候,如果遇到一個共享鎖正鎖定著自己需要的資源的時候,自己可以再加一個共享鎖,不過不能加排他鎖。但是,如果遇到自己需要鎖定的資源已經被一個排他鎖佔有之後,則只能等待該鎖定釋放資源之後自己才能獲取鎖定資源並添加自己的鎖定。如果一個事務請求的鎖模式與當前的鎖兼容,InnoDB就將請求的鎖授予該事務;反之,如果兩者不兼容,該事務就要等待鎖釋放。

2.3 行鎖優化建議

InnoDB行鎖是通過給索引上的索引項加鎖來實現的,只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖。在實際應用中,要特別注意InnoDB行鎖的這一特性,不然的話,可能導致大量的鎖衝突,從而影響並發性能。下面通過一些實際例子來加以說明。

1、在不通過索引條件查詢的時候,InnoDB確實使用的是表鎖,而不是行鎖;

2、由於MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的查詢條件,是會出現鎖衝突的;

3、當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會使用行鎖來對數據加鎖;

4、即便在條件中使用了索引欄位,但是否使用索引來檢索數據是由MySQL通過判斷不同執行計劃的代價來決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖;

2.4 間隙鎖

當我們用範圍條件而不是相等條件檢索數據並請求共享鎖或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫做「間隙(GAP)」,InnoDB也會對這個「間隙」加鎖,這種鎖機制就是間隙鎖。

假如userInfo表中只有50條記錄,其userId的值分別是1,2,3,....,49,50;

下面的SQL:

mysql> select * from emp where userId > 49 for update;

是一個範圍條件的檢索,InnoDB不僅會對符合條件的userId值為50的記錄加鎖,也會對userId大於50(這些記錄並不存在)的「間隙」加鎖。

InnoDB使用間隙鎖的目的是防止幻讀,以滿足相關隔離級別的要求。對於上面的例子,如果不使用間隙鎖,若其他事務插入了userId大於50的任何記錄,那麼本事務如果再次執行上述語句,就會發生幻讀;因為間隙鎖鎖定的是一個範圍,而不是具體的索引鍵所以在使用範圍條件檢索並鎖定記錄時,間隙鎖會將不存在的鍵值也會被無辜的鎖定,而造成在鎖定的時候無法插入鎖定鍵值範圍內的任何數據。因此,在實際應用開發中,尤其是並發插入比較多的應用,我們要盡量優化業務邏輯,盡量使用相等條件來訪問更新數據,避免使用範圍條件。還要特別說明的是,InnoDB除了通過範圍條件加鎖時使用間隙鎖外,如果使用相等條件請求給一個不存在的記錄加鎖,InnoDB也會使用間隙鎖。

2.5 行鎖優化建議

1、盡量讓所有的查詢條件都通過索引來完成,從而避免InnoDB因為無法通過索引鍵加鎖而升級為表級鎖定;

2、合理設計索引,讓InnoDB在索引鍵上面加鎖的時候儘可能準確,儘可能的縮小鎖定範圍,避免造成不必要的鎖定而影響其它SQL的執行;

3、減少基於範圍的數據檢索過濾條件,避免因為間隙鎖帶來的負面影響而鎖定了不該鎖定的記錄;

4、盡量控制事務的大小,減少鎖定的資源量和鎖定時間長度;

5、在業務環境允許的情況下,可使用較低級別的事務隔離,以減少MySQL因為實現事務隔離級別所帶來的附加成本;

6、類似業務模塊中,盡量按照相同的訪問順序來訪問,防止產生死鎖;

7、在同一個事務中,盡量做到一次鎖定所需要的所有資源,減少死鎖產生概率;

8、對於非常容易產生死鎖的業務部分,可以嘗試使用升級鎖定顆粒度,通過表級鎖定來減少死鎖產生的概率;

三、頁級鎖

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

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

總的來說,MySQL這3種鎖的特性可大致歸納如下:

表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,並發度最低;

行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,並發度也最高;

頁面鎖:開銷和加鎖時間界於表鎖和行鎖之間;會出現死鎖;鎖定粒度界於表鎖和行鎖之間,並發度一般。

四、行級鎖和表級鎖能共存嗎?

行級鎖與表級鎖是可以共存的。InnoDB為了讓表鎖和行鎖共存而引入了意向鎖。

舉個例子(此時假設行鎖和表鎖能共存): 事務A鎖住表中的一行(寫鎖)。事務B鎖住整個表(寫鎖)。

但你就會發現一個很明顯的問題,事務A既然鎖住了某一行,其他事務就不可能修改這一行。這與"事務B鎖住整個表就能修改表中的任意一行"形成了衝突。所以,沒有意向鎖的時候,行鎖與表鎖共存就會存在問題!

那麼意向鎖是如何讓表鎖和行鎖共存的?

有了意向鎖之後,前面例子中的事務A在申請行鎖(寫鎖)之前,資料庫會自動先給事務A申請表的意向排他鎖。當事務B去申請表的寫鎖時就會失敗,因為表上有意向排他鎖之後事務B申請表的寫鎖時會被阻塞。由此我們可得出意向鎖是表鎖。

(更多文章可關注微信公眾號:IT雞窩)

參考文章:

blog.csdn.net/zcl_love_cnblogs.com/sessionbest

推薦閱讀:

相關文章