1. 程式人生 > >Mysql的鎖機制解讀

Mysql的鎖機制解讀

最近系統多次因對資料庫鎖使用不當引起問題,故從基礎學習一下mysql鎖機制。

基本概念:

共享鎖

共享鎖的代號是S,是Share的縮寫,共享多的鎖粒度是行或者元組(多個行),一個事務獲取了共享多之後,可以對鎖定範圍內的資料執行讀操作。

排它鎖

排它鎖的代號是X,是eXclusive的縮寫,排他鎖的粒度是行或元組,與共享鎖相同,一個事務獲取了排它鎖之後,可以對鎖定範圍內的資料執行寫操作。

例:假設有兩個事務t1和t2

如果事務t1獲取了一個元組的共享鎖,事務t2還可以立即獲取這個元組的共享鎖,但不能立即獲取這個是元組的排它鎖,(必須等到t1釋放共享鎖之後,才能獲取這個元組的排它鎖)。

如果事務t1獲取了一個元組的排它鎖,事務t2不能立即獲取這個元組的共享鎖,也不能立即獲取這個元組的排它鎖,(必須等到t1釋放排它鎖之後)。

意向鎖

意向鎖是一種表鎖,鎖定的粒度是整張表,分為意向共享鎖(IS)和意向排它鎖(IX)兩類。

意向共享鎖表示一個事務“有意“對資料上共享鎖。“有意“這兩個字表達的意思比較微妙,說的明白點就是指事務想幹這個事但還沒有真去幹。

例:一個事務t1執行了這樣一個語句“select * from table lock in share model”,如果這個語句執行成功,就對錶table上了一個意向共享鎖。lock in share model就是說事務t1在接下來要執行的語句中要獲取S鎖。

意向排它鎖的含義同理可知,上例中獲取意向排它鎖,可以使用“select * from table for update

”。

lock in share model和for update這個兩個東西在資料理論中還有個學名交悲觀鎖,與悲觀鎖相對的當然還有樂觀鎖。可見各種鎖都是成雙成對出現的,關於悲觀鎖和樂觀鎖的問題暫且不表。

鎖的互斥和相容關係

鎖與鎖之間的關係,要麼相容,要麼互斥

鎖a和鎖b的相容是指:操作同樣一組資料時,如果事務t1獲取了鎖a,另一個事務t2還可以獲取鎖b。

鎖a和鎖b的互斥是指:操作同樣一組資料時,如果事務t1獲取了鎖a,另一個事務t2必須在t1釋放鎖a之後方可獲取鎖b。

上面的共享鎖、排它鎖、意向共享鎖,意向排它鎖相互之間都是有相容/互斥關係的,可以用一個相容性矩陣表示(y表示相容,n表示不相容):

X S IX IS
X n n n n
S n y n y
IX n n y y
IS n y y y

相容矩陣為什麼這個樣子的?

X和S的相互關係在上文中解釋過了,IX和IS的相互關係全是相容的,這也好理解,因為他們都只是“有意”,還處於YY階段,沒有真幹,所以是可以相容的。剩下的就是X和IX,X和IS,S和IX,S和IS的關係了,我們可以由X和S的關推倒出這四組關係。

簡單的說:X和IX的關係=X和X的關係,因為事務在獲取IX鎖後,接下來就有權利獲取X鎖。如果X和IX相容的話,就會出現兩個事務都獲取了X的情況,這與我們已知的X與X互斥是矛盾的,所以X與IX只能是互斥關係。其餘三組同理可知。

MySQL三種鎖級別

MySQL有三種鎖級別:表級鎖(table-level locking),頁級鎖(page-levellocking),行級鎖(row-levellocking

三種鎖的特性可大致歸納如下:

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

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

頁級鎖:開銷大小、加鎖速度、鎖定粒度、發生所衝突的概率及併發度都介於表級鎖與行級鎖之間,會出現死鎖。

而MySQL的儲存引擎中:MyISAM和MEMORY採用表級鎖;BDB採用(預設)頁級鎖,但也支援表級鎖;InnoDB採用(預設)行級鎖,但也支援表級鎖;

一、MyISAM表鎖

MyISAM儲存引擎只支援表鎖

1、查詢表級鎖的爭用情況

可以通過查詢table_locks_waited(表示不能立即獲取鎖的次數)和table_locks_immediate(表示可以立即獲取鎖的查詢次數)狀態變數來分析系統上的表鎖定爭奪

mysql>show status like"table%";
+———————–+———-+
| Variable_name | Value |
+———————–+———-+
| Table_locks_immediate | 76939364 |
| Table_locks_waited | 305089 |
+———————–+———-+
2 rows in set (0.00 sec)

Table_locks_waited的值比較高,說明存在著較嚴重的表級鎖爭用情況。

2、表級鎖的鎖模式

表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table WriteLock)。MyISAM在執行查詢語句(SELECT)前,會自動給涉及的所有表加讀鎖,而在執行(UPDATE,INSERT)前,會自動給涉及的所有表加寫鎖。

所以對MyISAM表進行操作,會有以下幾種情況:

a、對MyISAM表的讀操作(加讀鎖),不會組三其他程序對同一表的讀請求,但會阻塞對同一表的寫請求。只有當讀鎖釋放後才會執行其他程序的寫操作。

b、對MyISAM表的寫操作(加寫鎖),會組三其他程序對同一表的讀和寫操作,只有當寫鎖釋放後才會執行其他程序的讀寫操作。

3、併發插入

原則上資料表有一個讀鎖時,其他程序無法對此表進行寫操作,但在一定條件下,MyISAM表也支援查詢和插入操作的併發進行。MyISAM有一個系統變數concurrent_insert,專門用以控制其併發插入的行為,其值分別可以為0、1、2

a、當concurrent_insert為0時,不允許併發插入。

b、當concurrent_insert1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),MyISAM允許在一個程序讀表的同時,另一個程序從表尾插入記錄。這個是MyISAM的預設設定。

c、當concurrent_insert為2時,無論MyISAM表中有沒有空洞,都允許下表尾併發插入

4、鎖排程

由於MySQL認為寫請求一般比讀請求重要,所以如果讀寫請求同時進行的話,MySQL將會優先執行寫操作。這樣MyISAM表在進行大量的寫操作時(特別是更新的欄位存在索引的情況下),會造成查詢操作很難獲取讀鎖,從而導致查詢阻塞。

我們可以通過一些設定來除錯MyISAM的排程行為:

a、通過指定啟動引數low-priority-updates,使MyISAM引擎預設給予讀請求以優先的權利。

b、通過執行命令SET LOW_PRIORITY_UPDATES=1,使該連結發出的更新請求有限級降低。

c、通過INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,降低該語句的優先順序。

上面三種方法都是要麼更新優先,要麼查詢優先的方法。這裡要說明的是,不要盲目的給MySQL設定為讀優先,因為一些需要長時間執行的查詢操作,也會使程序“餓死”。只有根據你你的實際情況,來決定設定那種操作優先。這些方法還是沒有從根本上同時解決查詢和更新的問題。

在一個有大資料量高併發的MySQL裡,我們還可以採用另一種策略來進行優化,那就是通過MySQL主從(讀寫)分離來實現負載均衡,這樣可以避免優先哪種操作從而可能導致另一種操作的阻塞,下面將用一個篇幅來說明MySQL的讀寫分離。