mysql的鎖機制之表鎖

MySQL各存儲引擎使用了三種類型(級別)的鎖定機制:表級鎖定,行級鎖定和頁級鎖定.
表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖衝突的概率最高,併發度最低。
行級鎖:開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度也最高。

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

本文主要介紹表鎖的類型,機制,創建,解鎖,優化思路等內容.

1.表級鎖定(table-level)的定義:
表級鎖定的定義:表級別的鎖定是MySQL各存儲引擎中最大顆粒度的鎖定機制。該鎖定機制最大的特點是實現邏輯非常簡單,帶來的系統負面影響最小。所以獲取鎖和釋放鎖的速度很快。由於表級鎖一次會將整個表鎖定,所以可以很好的避免困擾我們的死鎖問題。當然,鎖定顆粒度大所帶來最大的負面影響就是出現鎖定資源爭用的概率也會最高,致使並大度大打折扣。

使用表級鎖定的主要存儲引擎是MyISAM,MEMORY,CSV等一些非事務性存儲引擎。

2.表鎖定的類型

表級鎖定主要分爲兩種類型,一種是讀鎖定,另一種是寫鎖定 .
意思是假如用戶對一個表進行讀鎖定,其他用戶有讀的權利,不能寫.
假如用戶對一個表進行寫鎖定,其他用戶不能讀也不能寫.
用戶對錶進行讀寫都是有條件的:
一個新的客戶端請求在申請獲取讀鎖定資源的時候,需要滿足兩個條件:
1.資源沒有被寫鎖定
2.寫鎖定等待隊列中沒有更高優先級的寫鎖定等待
如果滿足了上面兩個條件之後,該請求會被立即通過,並將相關的信息存入讀隊列中,否則會被迫進入等待隊列中等待資源的釋放。
一個新的客戶端請求在申請獲取寫鎖定資源的時候,被申請資源需要滿足三個條件:

1.沒被寫鎖定2.沒被寫鎖定等待3.沒被讀鎖定

3.兩種表鎖的管理機制

上述所說的隊列,大概有四種,他們用來維護兩種鎖定狀態.鎖類似於一個倉庫的大門,而這些隊列爲門衛.維持倉庫的狀態和人們的行爲.
在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) 等待寫鎖的所有線程
當前持有讀鎖的所有線程的相關信息都能夠在Currentread-lockqueue中找到,隊列中的信息按照獲取到鎖的時間依序存放。而正在等待鎖定資源的信息則存放在Pendingread-lockqueue裏面,另外兩個線程存放寫鎖信息的隊列也按照上面相同規則來存放信息.
4.表鎖的創建,查看,解鎖.
(1)
表鎖的創建
lock tables 表名 [read | write]
lock tables 表名1 [read | write], 表名2 [read | write]...... 
上述語句中名詞解釋
read爲讀鎖,加了之後當前會話和其他會話都只能讀不能寫。
write爲寫鎖,加了之後只有當前會話可以讀和寫。
加單表用lock table,加多表用lock tables(table與tables作用完全相同,只
是語義化而已) .
(2)表鎖的解除
語法:unlock table | unlock tables:
說明:釋放被當前會話持有的任何鎖。 
(3)查看當前的表鎖

示例:show open tables where in_use > 0; 


說明:

Database:包含該表的數據庫

Table:表名

In_use:表中有表鎖或鎖請求的數量(處於鎖等待中的鎖也參與計數)

Name_locked:表名是否被鎖定。名稱鎖定用於諸如刪除或重命名錶等操作

(4)表鎖爭用情況查看

語法:show status like 'table%'; 

說明:

Table_locks_immediate:已授予表鎖請求的次數。

Table_locks_waited:處於鎖等待中的表鎖數量

5.表鎖優化建議

(1)如上圖所示表鎖爭用的情況.

兩個狀態值都是從系統啓動後開始記錄,出現一次對應的事件則數量加1。如果這裏的Table_locks_waited狀態值比較高,那麼說明系統中表級鎖定爭用現象比較嚴重,就需要進一步分析爲什麼會有較多的鎖定資源爭用了。

2)縮短鎖定時間

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

如何讓鎖定時間儘可能的短呢?唯一的辦法就是讓我們的Query執行時間儘可能的短。

a)盡兩減少大的複雜Query,將複雜Query分拆成幾個小的Query分佈進行;

b)儘可能的建立足夠高效的索引,讓數據檢索更迅速;

c)儘量讓用到表鎖定的表只存放必要的信息,控制字段類型;

d)利用合適的機會優化表數據文件。

3)分離能並行的操作

說到MyISAM存儲引擎的表鎖,而且是讀寫互相阻塞的表鎖,可能有些人會認爲在MyISAM存儲引擎的表上就只能是完全的串行化,沒辦法再並行了。大家不要忘記了,MyISAM的存儲引擎還有一個非常有用的特性,那就是ConcurrentInsert(併發插入)的特性。

MyISAM存儲引擎有一個控制是否打開Concurrent Insert功能的參數選項:concurrent_insert,可以設置爲01或者2。三個值的具體說明如下:

concurrent_insert=2,無論MyISAM表中有沒有空洞,都允許在表尾併發插入記錄;

concurrent_insert=1,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個進程讀表的同時,另一個進程從表尾插入記錄。這也是MySQL的默認設置;

concurrent_insert=0,不允許併發插入。

可以利用MyISAM存儲引擎的併發插入特性,來解決應用中對同一表查詢和插入的鎖爭用。例如,將concurrent_insert系統變量設爲2,總是允許併發插入;同時,通過定期在系統空閒時段執行OPTIMIZE TABLE語句來整理空間碎片,收回因刪除記錄而產生的中間空洞。

4)合理利用讀寫優先級

MyISAM存儲引擎的是讀寫互相阻塞的,那麼,一個進程請求某個MyISAM表的讀鎖,同時另一個進程也請求同一表的寫鎖,MySQL如何處理呢?

答案是寫進程先獲得鎖。不僅如此,即使讀請求先到鎖等待隊列,寫請求後到,寫鎖也會插到讀鎖請求之前。

這是因爲MySQL的表級鎖定對於讀和寫是有不同優先級設定的,默認情況下是寫優先級要大於讀優先級。

所以,如果我們可以根據各自系統環境的差異決定讀與寫的優先級:

通過執行命令SET LOW_PRIORITY_UPDATES=1,使該連接讀比寫的優先級高。如果我們的系統是一個以讀爲主,可以設置此參數,如果以寫爲主,則不用設置;

通過指定INSERTUPDATEDELETE語句的LOW_PRIORITY屬性,降低該語句的優先級。

雖然上面方法都是要麼更新優先,要麼查詢優先的方法,但還是可以用其來解決查詢相對重要的應用(如用戶登錄系統)中,讀鎖等待嚴重的問題。

另外,MySQL也提供了一種折中的辦法來調節讀寫衝突,即給系統參數max_write_lock_count設置一個合適的值,當一個表的讀鎖達到這個值後,MySQL就暫時將寫請求的優先級降低,給讀進程一定獲得鎖的機會。

這裏還要強調一點:一些需要長時間運行的查詢操作,也會使寫進程“餓死”,因此,應用中應儘量避免出現長時間運行的查詢操作,不要總想用一條SELECT語句來解決問題,因爲這種看似巧妙的SQL語句,往往比較複雜,執行時間較長,在可能的情況下可以通過使用中間表等措施對SQL語句做一定的“分解”,使每一步查詢都能在較短時間完成,從而減少鎖衝突。如果複雜查詢不可避免,應儘量安排在數據庫空閒時段執行,比如一些定期統計可以安排在夜間執行。


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