Mysql 鎖機制筆記

MYSQL 鎖機制

數據庫在對資源進行高并發的讀寫操作時,為了保證數據的一致性,有效性,鎖是很重要的機制。Mysql的鎖分為三個級別:行級鎖,頁級鎖,表級鎖。對于平時常用的存儲引擎,MyISAM采用的是表級鎖,InnoDB采用的是行級鎖加表級鎖,而支持頁級鎖的BDB引擎已經逐漸被InnoDB替代了,這里暫不討論。

表級鎖的開銷小,加鎖快,不會出現死鎖,鎖定粒度大,大概率發生鎖的沖突,并發度低
行級鎖的開銷大,加鎖滿,會出現死鎖,鎖定粒度小,小概率發生鎖的重讀,并發度高
上述特點來看,很難說哪種鎖更好,只能相對于所處的業務場景來選擇更加適合的鎖機制。如果僅從鎖的角度來看,表級鎖更適合以查詢為主的應用場景,而行級鎖則更適合于大量按索引條件并發更新少量數據的應用場景。

MyISAM表

MyISAM存儲引擎只支持表級鎖,鎖的模式有共享鎖和排他鎖。共享鎖是他人可以讀但不能寫,排它鎖則會阻塞他人的讀寫操作。MyISAM的讀寫之間,以及寫寫之間是串行的。
MyISAM在執行SQL語句時,會自動為SELECT語句加上共享鎖,為UDI操作加上排它鎖。MYSQL不支持鎖升級,如果涉及到更新操作,需要在一開始就加上排它鎖。

MyISAM的并發插入

在存儲引擎中有一個系統變量concurrent_insert,專門控制其并發插入的行為
concurrent_insert=0時,不允許并發插入
concurrent_insert=1時,如果MyISAM表中沒有空洞(即表的中間沒有被刪除的行),其允許在一個進程讀表的同事,另一個進程從表尾插入記錄,這也是MySQL的默認設置
concurrent_insert=2時,如果MyISAM表中沒有空洞,允許在表尾并發插入記錄

MyISAM的鎖調度

在MyISAM存儲引擎中,寫的重要性要大于讀,所以在操作隊列中,即使寫的操作在讀的操作之后,也會讓寫先拿到排它鎖,這也正是MyISAM不適合于大量寫入操作的應用場景的原因,這樣可能會導致讀操作永遠在阻塞中,永遠在等待寫操作的釋放鎖。當然,除了默認的設置,可以通過設置語句的優先級別來管理這個執行順序

InnoDB

他與MyISAM的最大區別有兩個方面,一個是支持事務,另一個是采用了行級鎖
事務的并發處理會帶來幾個問題
1.不可重復讀,A事務在第一次讀和第二次讀之間,如果B對數據進行的修改,則兩次讀取的數據會不一致
2.更新丟失,A和B同時操作一個數據,最后執行完畢的會覆蓋前一個的執行結果
3.臟讀,A事務添加了數據但并未提交,B讀到了這條數據后A回滾了,就會導致臟讀(很形象)
4.幻讀,A事務第二次讀取數據之前,B數據提交了滿足條件的數據,這種現象就叫幻讀

為了解決以上問題,產生了四個隔離級別:未提交讀,提交讀,可重復讀(InnoDB事務默認使用),串行讀

鎖模式

共享鎖(S):允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。
排他鎖(X):允許獲得排他鎖的事務更新數據,阻止其他事務取得相同數據集的共享讀鎖和排他寫鎖。
另外,為了允許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖。
意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。

語句示例:
共享鎖(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他鎖(X):SELECT * FROM table_name WHERE ... FOR UPDATE。

行鎖的實現方式:

InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不同,后者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味著:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!在實際應用中,要特別注意InnoDB行鎖的這一特性,不然的話,可能導致大量的鎖沖突,從而影響并發性能。

由于MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的索引鍵,是會出現鎖沖突的

當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會使用行鎖來對數據加鎖。如果不同的索引碰巧都落到了同一個行上,那么同樣會阻塞。

即便在條件中使用了索引字段,但是否使用索引來檢索數據是由MySQL通過判斷不同執行計劃的代價來決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖。因此,在分析鎖沖突時,別忘了檢查SQL的執行計劃,以確認是否真正使用了索引。

間隙鎖

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

舉例來說,假如emp表中只有101條記錄,其empid的值分別是 1,2,...,100,101,下面的SQL:
Select * from emp where empid > 100 for update;
是一個范圍條件的檢索,InnoDB不僅會對符合條件的empid值為101的記錄加鎖,也會對empid大于101(這些記錄并不存在)的“間隙”加鎖。
InnoDB使用間隙鎖的目的,一方面是為了防止幻讀,以滿足相關隔離級別的要求,對于上面的例子,要是不使用間隙鎖,如果其他事務插入了empid大于100的任何記錄,那么本事務如果再次執行上述語句,就會發生幻讀;另外一方面,是為了滿足其恢復和復制的需要

還要特別說明的是,InnoDB除了通過范圍條件加鎖時使用間隙鎖外,如果使用相等條件請求給一個不存在的記錄加鎖,InnoDB也會使用間隙鎖!

MySQL的恢復機制是通過BINLOG記錄來執行IUD操作來同步Slave的,這就要求:在一個事務未提交前,其他并發事務不能插入滿足其鎖定條件的任何記錄,也就是不允許出現幻讀,這已經超過了ISO/ANSI SQL92“可重復讀”隔離級別的要求,實際上是要求事務要串行化。這也是許多情況下,InnoDB要用到間隙鎖的原因,比如在用范圍條件更新記錄時,無論在Read Commited或是Repeatable Read隔離級別下,InnoDB都要使用間隙鎖,但這并不是隔離級別要求的。

INSERT...SELECT...和 CREATE TABLE...SELECT...語句,可能會阻止對源表的并發更新,造成對源表鎖的等待。如果查詢比較復雜的話,會造成嚴重的性能問題,我們在應用中應盡量避免使用。實際上,MySQL將這種SQL叫作不確定(non-deterministic)的SQL,不推薦使用。

什么時候使用表鎖

對于InnoDB表,在絕大部分情況下都應該使用行級鎖,因為事務和行鎖往往是我們之所以選擇InnoDB表的理由。但在個別特殊事務中,也可以考慮使用表級鎖。
第一種情況是:事務需要更新大部分或全部數據,表又比較大,如果使用默認的行鎖,不僅這個事務執行效率低,而且可能造成其他事務長時間鎖等待和鎖沖突,這種情況下可以考慮使用表鎖來提高該事務的執行速度。
第二種情況是:事務涉及多個表,比較復雜,很可能引起死鎖,造成大量事務回滾。這種情況也可以考慮一次性鎖定事務涉及的表,從而避免死鎖、減少數據庫因事務回滾帶來的開銷。
如果以上兩種事務過多,那我們就可以考慮使用MyISAM引擎了

死鎖

發生死鎖后,InnoDB一般都能自動檢測到,并使一個事務釋放鎖并回退,另一個事務獲得鎖,繼續完成事務。但在涉及外部鎖,或涉及表鎖的情況下,InnoDB并不能完全自動檢測到死鎖,這需要通過設置鎖等待超時參數 innodb_lock_wait_timeout來解決。需要說明的是,這個參數并不是只用來解決死鎖問題,在并發訪問比較高的情況下,如果大量事務因無法立即獲得所需的鎖而掛起,會占用大量計算機資源,造成嚴重性能問題,甚至拖跨數據庫。我們通過設置合適的鎖等待超時閾值,可以避免這種情況發生。

在了解InnoDB鎖特性后,用戶可以通過設計和SQL調整等措施減少鎖沖突和死鎖,包括:

  1. 盡量使用較低的隔離級別;
  2. 精心設計索引,并盡量使用索引訪問數據,使加鎖更精確,從而減少鎖沖突的機會;
  3. 選擇合理的事務大小,小事務發生鎖沖突的幾率也更小;
  4. 給記錄集顯示加鎖時,最好一次性請求足夠級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖;
  5. 不同的程序訪問一組表時,應盡量約定以相同的順序訪問各表,對一個表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會;
  6. 盡量用相等條件訪問數據,這樣可以避免間隙鎖對并發插入的影響;
  7. 不要申請超過實際需要的鎖級別;除非必須,查詢時不要顯示加鎖;
  8. 對于一些特定的事務,可以使用表鎖來提高處理速度或減少死鎖的可能。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容