參考鏈接:
https://www.cnblogs.com/leedaily/p/8378779.html
http://www.lxweimin.com/p/95a0d10eb881
小結
對于MyISAM的表鎖,主要討論了以下幾點:
(1)共享讀鎖(S)之間是兼容的,但共享讀鎖(S)與排他寫鎖(X)之間,以及排他寫鎖(X)之間是互斥的,也就是說讀和寫是串行的。
(2)在一定條件下,MyISAM允許查詢和插入并發執行,我們可以利用這一點來解決應用中對同一表查詢和插入的鎖爭用問題。
(3)MyISAM默認的鎖調度機制是寫優先,這并不一定適合所有應用,用戶可以通過設置LOW_PRIORITY_UPDATES參數,或在INSERT、UPDATE、DELETE語句中指定LOW_PRIORITY選項來調節讀寫鎖的爭用。
(4)由于表鎖的鎖定粒度大,讀寫之間又是串行的,因此,如果更新操作較多,MyISAM表可能會出現嚴重的鎖等待,可以考慮采用InnoDB表來減少鎖沖突。
對于InnoDB表,本文主要討論了以下幾項內容:
(1)InnoDB的行鎖是基于索引實現的,如果不通過索引訪問數據,InnoDB會使用表鎖。
(2)介紹了InnoDB間隙鎖(Next-key)機制,以及InnoDB使用間隙鎖的原因。
在不同的隔離級別下,InnoDB的鎖機制和一致性讀策略不同。
在了解InnoDB鎖特性后,用戶可以通過設計和SQL調整等措施減少鎖沖突和死鎖,包括:
盡量使用較低的隔離級別; 精心設計索引,并盡量使用索引訪問數據,使加鎖更精確,從而減少鎖沖突的機會;
選擇合理的事務大小,小事務發生鎖沖突的幾率也更小;
給記錄集顯式加鎖時,最好一次性請求足夠級別的鎖。比如要修改數據的話,最好直接申請排他鎖,而不是先申請共享鎖,修改時再請求排他鎖,這樣容易產生死鎖;
不同的程序訪問一組表時,應盡量約定以相同的順序訪問各表,對一個表而言,盡可能以固定的順序存取表中的行。這樣可以大大減少死鎖的機會;
盡量用相等條件訪問數據,這樣可以避免間隙鎖對并發插入的影響; 不要申請超過實際需要的鎖級別;除非必須,查詢時不要顯示加鎖;
對于一些特定的事務,可以使用表鎖來提高處理速度或減少死鎖的可能。
相關概念
(1)共享鎖
共享鎖(s):又稱讀鎖。允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。若事務T對數據對象A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖。這保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。
(2)排它鎖
排他鎖(X):又稱寫鎖。允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的數據集共享讀鎖和排他寫鎖。若事務T對數據對象A加上X鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖。
對于排他鎖大家的理解可能就有些差別,我當初就犯了一個錯誤,以為排他鎖鎖住一行數據后,其他事務就不能讀取和修改該行數據,其實不是這樣的。排他鎖指的是一個事務在一行數據加上排他鎖后,其他事務不能再在其上加其他的鎖。mysql InnoDB引擎默認的修改數據語句:update,delete,insert都會自動給涉及到的數據加上排他鎖,select語句默認不會加任何鎖類型,如果加排他鎖可以使用select …for update語句,加共享鎖可以使用select … lock in share mode語句。所以加過排他鎖的數據行在其他事務種是不能修改數據的,也不能通過for update和lock in share mode鎖的方式查詢數據,但可以直接通過select …from…查詢數據,因為普通查詢沒有任何鎖機制。
(3)獨占鎖
同排它鎖
(4)臨鍵鎖
https://blog.csdn.net/weixin_34006468/article/details/88039873
Next-Key 可以理解為一種特殊的間隙鎖,也可以理解為一種特殊的算法。通過臨建鎖可以解決幻讀的問題。 每個數據行上的非唯一索引列上都會存在一把臨鍵鎖,當某個事務持有該數據行的臨鍵鎖時,會鎖住一段左開右閉區間的數據。需要強調的一點是,InnoDB 中行級鎖是基于索引實現的,臨鍵鎖只與非唯一索引列有關,在唯一索引列(包括主鍵列)上不存在臨鍵鎖。
(5)間隙鎖
當我們用范圍條件而不是相等條件檢索數據,并請求共享或排他鎖時,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這種加鎖機制會阻塞符合條件范圍內鍵值的并發插入,這往往會造成嚴重的鎖等待。因此,在實際應用開發中,尤其是并發插入比較多的應用,我們要盡量優化業務邏輯,盡量使用相等條件來訪問更新數據,避免使用范圍條件。
還要特別說明的是,InnoDB除了通過范圍條件加鎖時使用間隙鎖外,如果使用相等條件請求給一個不存在的記錄加鎖,InnoDB也會使用間隙鎖!
(6)自增鎖
https://blog.csdn.net/qiuyepiaoling/article/details/49923703
5.1.22之前InnoDB自增值是通過其本身的自增長計數器來獲取值,該實現方式是通過表鎖機制來完成的(AUTO-INC LOCKING),鎖不是在每次事務完成后釋放,而是在完成對自增長值插入的SQL語句后釋放,要等待其釋放才能進行后續操作。比如說當表里有一個auto_increment字段的時候,innoDB會在內存里保存一個計數器用來記錄auto_increment的值,當插入一個新行數據時,就會用一個表鎖來鎖住這個計數器,直到插入結束。如果大量的并發插入,表鎖會引起SQL堵塞。
在5.1.22之后,InnoDB為了解決自增主鍵鎖表的問題,引入了參數innodb_autoinc_lock_mode,該實現方式是通過輕量級互斥量的增長機制完成的。它是專門用來在使用auto_increment的情況下調整鎖策略
innodb_autoinc_lock_mode:可以設定3個值,0,1,2
0:traditonal 通過表鎖的方式進行,所有類型的insert都用AUTO-inc locking。
1:consecutive 默認值,產生一個輕量鎖,對于simple insert 自增長值的產生使用互斥量對內存中的計數器進行累加操作,對于bulk insert 則還是使用表鎖的方式進行。
2:interleaved 對所有的insert-like 自增長值的產生使用互斥量機制完成,并發性能最高,并發插入可能導致自增值不連續,可能會導致Statement 的 Replication 出現不一致,使用該模式,需要用 Row Replication的模式。
(7)意向鎖(表級鎖)
意向共享鎖(IS):事務打算給數據行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
意向排他鎖(IX):事務打算給數據行加排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
如果一個事務請求的鎖模式與當前的鎖兼容,InnoDB就請求的鎖授予該事務;反之,如果兩者兩者不兼容,該事務就要等待鎖釋放。
意向鎖是InnoDB自動加的,不需用戶干預。對于UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及數據集加排他鎖(X);對于普通SELECT語句,InnoDB不會加任何鎖
詳細解釋:https://www.zhihu.com/question/51513268
優化建議
https://www.cnblogs.com/yuxiang1/p/11090335.html
InnoDB中:
盡可能讓所有數據檢索都通過索引來完成,避免無索引行鎖升級為表鎖;
盡可能較少檢索條件,避免間隙鎖;
盡量控制事務大小,減少鎖定資源量和時間長度;
鎖住某行后,盡量不要去調別的行或表,趕緊處理被鎖住的行然后釋放掉鎖;
涉及相同表的事務,對于調用表的順序盡量保持一致;
在業務環境允許的情況下,盡可能低級別事務隔離;
由于Innodb的行級鎖定和事務性,所以肯定會產生死鎖,下面是一些比較常用的減少死鎖產生概率
a. 類似業務模塊中,盡可能按照相同的訪問順序來訪問,防止產生死鎖;
b. 在同一個事務中,盡可能做到一次鎖定所需要的所有資源,減少死鎖產生概率;
c. 對于非常容易產生死鎖的業務部分,可以嘗試使用升級鎖定顆粒度,通過表級鎖定來減少死鎖產生的概率;
參數查閱
系統鎖定爭用情況查詢對于兩種鎖定級別,MySQL內部有兩組專門的狀態變量記錄系統內部鎖資源爭用情況
MySQL 實現的表級鎖定的爭用狀態變量:
Table_locks_immediate:產生表級鎖定的次數;
Table_locks_waited:出現表級鎖定爭用而發生等待的次數;
對于Innodb所使用的行級鎖定,系統中是通過另外一組更為詳細的狀態變量來記錄的,如下:
Innodb 的行級鎖定狀態變量不僅記錄了鎖定等待次數,還記錄了鎖定總時長,每次平均時長,以及最大時長,此外還有一個非累積狀態量顯示了當前正在等待鎖定的等待數量。對各個狀態量的說明如下:
Innodb_row_lock_current_waits:當前正在等待鎖定的數量;
Innodb_row_lock_time:從系統啟動到現在鎖定總時間長度;
Innodb_row_lock_time_avg:每次等待所花平均時間;
Innodb_row_lock_time_max:從系統啟動到現在等待最常的一次所花的時間;
Innodb_row_lock_waits:系統啟動后到現在總共等待的次數;
對于這5個狀態變量,比較重要的主要是Innodb_row_lock_time_avg(等待平均時長),Innodb_row_lock_waits(等待總次數)以及Innodb_row_lock_time(等待總時長)這三項。尤其是當等待次數很高,而且每次等待時長也不小的時候,我們就需要分析系統中為什么會有如此多的等待,然后根據分析結果著手指定優化計劃。
此外,Innodb除了提供這五個系統狀態變量之外,還提供的其他更為豐富的即時狀態信息供我們分析使用。可以通過如下方法查看
1.通過創建InnodbMonitor表來打開Innodb的monitor功能:
2.然后通過使用“show engine innodb status;”查看細節信息(由于輸出內容太多就不在此記錄了)