mysql锁历史记录_史上最全MySQL锁机制
MyISAM的鎖調度
MyISAM存儲引擎的讀鎖和寫鎖是互斥的,讀寫操作是串行的。但它認為寫鎖的優先級比讀鎖高,所以即使讀請求先到鎖等待隊列,寫請求后到,寫鎖也會插到讀鎖請求之前!這也正是MyISAM表不太適合于有大量更新操作和查詢操作應用的原因,因為,大量的更新操作會造成查詢操作很難獲得讀鎖,從而可能永遠阻塞。可以通過一些設置來調節MyISAM的調度行為。
通過指定啟動參數low-priority-updates,使MyISAM引擎默認給予讀請求以優先的權利。
通過執行命令SET LOW_PRIORITY_UPDATES=1,使該連接發出的更新請求優先級降低。
通過指定INSERT、UPDATE、DELETE語句的LOW_PRIORITY屬性,降低該語句的優先級。
雖然上面3種方法都是要么更新優先,要么查詢優先的方法,但還是可以用其來解決查詢相對重要的應用(如用戶登錄系統)中,讀鎖等待嚴重的問題。另外,MySQL也提供了一種折中的辦法來調節讀寫沖突,即給系統參數max_write_lock_count設置一個合適的值,當一個表的讀鎖達到這個值后,MySQL就暫時將寫請求的優先級降低,給讀進程一定獲得鎖的機會。上面已經討論了寫優先調度機制帶來的問題和解決辦法。
這里還要強調一點:一些需要長時間運行的查詢操作,也會使寫進程“餓死”!因此,應用中應盡量避免出現長時間運行的查詢操作,不要總想用一條SELECT語句來解決問題,因為這種看似巧妙的SQL語句,往往比較復雜,執行時間較長,在可能的情況下可以通過使用中間表等措施對SQL語句做一定的“分解”,使每一步查詢都能在較短時間完成,從而減少鎖沖突。如果復雜查詢不可避免,應盡量安排在數據庫空閑時段執行,比如一些定期統計可以安排在夜間執行。
InnoDB
InnoDB與MyISAM的最大不同有兩點:一是支持事務(TRANSACTION);二是采用了行級鎖。行級鎖與表級鎖本來就有許多不同之處,另外,事務的引入也帶來了一些新問題。
事務概念
學習Spring的時候,一般通過注解@Transitional就能啟動spring的事務管理,在MySQL中也同樣支持事務的四個原則ACID:
**A(Atomicity)原子性:**事務是一個原子操作單元,其對數據的修改,要么全都執行,要么全都不執行。
**C(Consistent)一致性:**在事務開始和完成時,數據都必須保持一致狀態。這意味著所有相關的數據規則都必須應用于事務的修改,以保持數據的完整性;事務結束時,所有的內部數據結構(如B樹索引或雙向鏈表)也都必須是正確的。
**I(Isolation)隔離性:**數據庫系統提供一定的隔離機制,保證事務在不受外部并發操作影響的“獨立”環境執行。這意味著事務處理過程中的中間狀態對外部是不可見的,反之亦然。
**D(Durable)持久性:**事務完成之后,它對于數據的修改是永久性的,即使出現系統故障也能夠保持。并發事務處理帶來的問題
相對于串行處理來說,并發事務處理能大大增加數據庫資源的利用率,提高數據庫系統的事務吞吐量,從而可以支持更多的用戶。但并發事務處理也會帶來一些問題,主要包括以下幾種情況。
更新丟失(Last update):A和B同時對一行數據進行處理,A修改后進行保存,然后B修改后進行保存,這樣A的更新被覆蓋了,相當于發生丟失更新的問題。所以可以在A事務未結束前,B不能訪問該記錄,這樣就能避免更新丟失的問題。
臟讀(Dirty Reads):A事務在對一條記錄做修改,但還未提交,這條記錄處于不一致的狀態;這時,B事務也來讀同一條記錄,這時如果沒有加控制,B讀了未修改前的數據,并根據該數據進行進一步處理,就會產生未提交的數據依賴關系。這種現象叫做“臟讀”
不可重復讀(Non-Repeatable Reads):B事務在讀取某些數據后的某個時間,再次讀取以前讀過的數據,卻發現其讀出的數據已經發生了改變(被更新或者刪除了,例如A事務修改了)。這種現象叫做“不可重復讀”。
幻讀(Phantom Reads):A事務按照相同查詢條件,重新讀取之前檢索過得內容,卻發現其它事務插入或修改其查詢條件的新數據,這種現象就叫”幻讀“。
事務的隔離級別
數據庫的事務隔離越嚴格,并發副作用越小,但付出的代價也就越大,因為事務隔離實質上就是使事務在一定程度上 “串行化”進行,這顯然與“并發”是矛盾的。同時,不同的應用對讀一致性和事務隔離程度的要求也是不同的,比如許多應用對“不可重復讀”和“幻讀”并不敏感,可能更關心數據并發訪問的能力。
4種隔離級別比較
讀數據一致性及允許的并發副作用
隔離級別
讀數據一致性
臟讀
不可重復讀
幻讀
未提交讀(Read uncommitted)
最低級別,只能保證不讀取
物理上損害的數據
是
是
是
已提交讀(Read committed)
語句級
否
是
是
可重復讀(Repeatable read)
事務級
否
否
是
可序列化(Serializable)
最高級別,事務級
否
否
否獲取InnoDB行鎖爭用情況
檢查InnoDB_row_lock狀態變量來分析:
mysql> show status like 'InnoDB_row_lock%';
+-------------------------------+-------+
| Variable_name | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0 |
| Innodb_row_lock_time | 0 |
| Innodb_row_lock_time_avg | 0 |
| Innodb_row_lock_time_max | 0 |
| Innodb_row_lock_waits | 0 |
+-------------------------------+-------+
5 rows in set (0.00 sec)
復制代碼
如果InnoDB_row_lock_waits和InnoDB_row_lock_time_avg的值比較高,表示鎖爭用情況比較嚴重。
InnoDB的行鎖模式以及加鎖方法
InnoDB實現了一下兩種類型的行鎖:
共享鎖(S):允許一個事務去多一行,阻止其它事務獲得相同數據集的排他鎖。
排他鎖(X): 允許獲得排他鎖的事務更新數據,阻止其它事務獲得相同數據集的共享鎖和排他寫鎖。
另外,為了允許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖。(感覺與MyISAM的表鎖機制類似)
意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
InnoDB行鎖模式兼容性列表:
請求鎖模式
矩陣結果表示是否兼容
當前鎖模式
X
IX
S
IS
X
沖突
沖突
沖突
沖突
IX
沖突
兼容
沖突
兼容
S
沖突
沖突
兼容
兼容
IS
沖突
兼容
兼容
兼容
如果一個事務請求的鎖模式與當前的鎖兼容,InnoDB就將請求的鎖授予該事務;反之,如果兩者不兼容,該事務就要等待鎖釋放。意向鎖是InnoDB自動加的;對于UPDATE、DELETE和INSERT語句,InnoDB會自動給設計數據集加排他鎖(X);對于普通的SELECT語句,InnoDB不會加鎖。可以通過以下語句顯示給記錄集加共享鎖或排他鎖:
共享鎖(S):SELECT * FROM TABLE_NAME WHERE ... LOCK IN SHARE MODE.
排他鎖(X):SELECT * FROM TABLE_NAME WHERE ... FOR UPDATE.
用SELECT ... IN SHARE MODE獲得共享鎖,主要用在需要數據依存關系時來確認某行記錄是否存在,并確保沒有人對這個記錄進行UPDATE或者DELETE操作。但是如果當前事務也需要對該記錄進行更新操作,則很有可能造成死鎖,對于鎖定行記錄后需要進行更新操作的應用,應該使用SELECT... FOR UPDATE方式獲得排他鎖。
所以在使用共享鎖模式下,查詢完數據后不要進行更新操作,不然又可能會造成死鎖;要更新數據,應該使用排他鎖模式。
InnoDB行鎖實現方式
InnoDB行鎖是通過給索引上的索引項加鎖來實現的,這一點MySQL與Oracle不同,后者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB這種行鎖實現特點意味著:只有通過索引條件檢索數據,InnoDB才使用行級鎖,否則,InnoDB將使用表鎖!(這個問題遇到過,由于沒加索引,行鎖變表鎖)
在不通過索引條件查詢的時候,InnoDB確實使用的是表鎖,而不是行鎖。
由于MySQL的行鎖是針對索引加的鎖,不是針對記錄加的鎖,所以雖然是訪問不同行的記錄,但是如果是使用相同的索引鍵,是會出現鎖沖突的。
當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,不論是使用主鍵索引、唯一索引或普通索引,InnoDB都會使用行鎖來對數據加鎖。
即便在條件中使用了索引字段,但是否使用索引來檢索數據是由MySQL通過判斷不同執行計劃的代價來決定的,如果MySQL認為全表掃描效率更高,比如對一些很小的表,它就不會使用索引,這種情況下InnoDB將使用表鎖,而不是行鎖。
可以通過explain執行計劃查看是否真正使用了索引。
間隙鎖(Next-key鎖)
當我們用范圍條件而不是相等條件檢索數據,并請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對于鍵值在條件范圍內但并不存在的記錄,叫做“間隙(GAP)”,InnoDB也會對這個“間隙”加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)。
舉個例子:假如emp表中只有101條記錄,其id的值從1~101,下面的sql:select * from emp where id > 100 for update;是范圍條件查詢,InnoDB不僅會對符合條件的id值為101的記錄加鎖,也會對id大于101(并不存在的值)的“間隙”加鎖。
結論:
很顯然,在使用范圍條件檢索并鎖定記錄時,InnoDB這種加鎖機制會阻塞符合條件范圍內鍵值的并發插入,這往往會造成嚴重的鎖等待。因此,在實際應用開發中,尤其是并發插入比較多的應用,我們要盡量優化業務邏輯,盡量使用相等條件來訪問更新數據,避免使用范圍條件。
關于死鎖(DeadLock)
上面知識點說過,MyISAM表鎖是deadlock free的,這是因為MyISAM總是一次獲得所需的全部鎖,要么全部滿足,要么等待,因此不會出現死鎖。但在InnoDB中,除單個SQL組成的事務外,鎖是逐步或得的,所以InnoDB發生死鎖是可能的。
舉個例子:
session A
session B
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from table_1 where where id=1 for update;
...
做一些其他處理...
mysql> set autocommit = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from table_2 where id=1 for update;
...
select * from table_2 where id =1 for update;
因session_2已取得排他鎖,等待
做一些其他處理...
mysql> select * from table_1 where where id=1 for update;
死鎖
也就是我們死鎖產生的條件,互相持有資源不釋放,還有環形等待。
發生死鎖后,InnoDB一般都能自動檢測到,并使一個事務釋放鎖并回退,另一個事務獲得鎖,繼續完成事務。但在涉及外部鎖,或涉及表鎖的情況下,InnoDB并不能完全自動檢測到死鎖,這需要通過設置鎖等待超時參數 innodb_lock_wait_timeout來解決。需要說明的是,這個參數并不是只用來解決死鎖問題,在并發訪問比較高的情況下,如果大量事務因無法立即獲得所需的鎖而掛起,會占用大量計算機資源,造成嚴重性能問題,甚至拖跨數據庫。我們通過設置合適的鎖等待超時閾值,可以避免這種情況發生。
避免死鎖的方法
在應用中,如果不同的程序會并發存取多個表,應盡量約定以相同的順序來訪問表,這樣可以大大降低產生死鎖的機會。在下面的例子中,由于兩個session訪問兩個表的順序不同,發生死鎖的機會就非常高!但如果以相同的順序來訪問,死鎖就可以避免。
在程序以批量方式處理數據的時候,如果事先對數據排序,保證每個線程按固定的順序來處理記錄,也可以大大降低出現死鎖的可能。
在事務中,如果要更新記錄,應該直接申請足夠級別的鎖,即排他鎖,而不應先申請共享鎖,更新時再申請排他鎖,因為當用戶申請排他鎖時,其他事務可能又已經獲得了相同記錄的共享鎖,從而造成鎖沖突,甚至死鎖。
在REPEATABLE-READ隔離級別下,如果兩個線程同時對相同條件記錄用SELECT...FOR UPDATE加排他鎖,在沒有符合該條件記錄情況下,兩個線程都會加鎖成功。程序發現記錄尚不存在,就試圖插入一條新記錄,如果兩個線程都這么做,就會出現死鎖。這種情況下,將隔離級別改成READ COMMITTED,就可避免問題。
當隔離級別為READ COMMITTED時,如果兩個線程都先執行SELECT...FOR UPDATE,判斷是否存在符合條件的記錄,如果沒有,就插入記錄。此時,只有一個線程能插入成功,另一個線程會出現鎖等待,當第1個線程提交后,第2個線程會因主鍵重出錯,但雖然這個線程出錯了,卻會獲得一個排他鎖!這時如果有第3個線程又來申請排他鎖,也會出現死鎖。對于這種情況,可以直接做插入操作,然后再捕獲主鍵重異常,或者在遇到主鍵重錯誤時,總是執行ROLLBACK釋放獲得的排他鎖
小結
這是一篇學習文章,關于MySQL的鎖機制又多了幾分了解,以后在寫SQL和排查問題時候,盡量避免死鎖和更快定位問題所在。
出處:https://juejin.im/post/5ce8eee45188253114078f2a
編輯:尹文敏
如何成為 DevOps 大牛?
DevOps 國際峰會 2019 · 北京站,
一線互聯網、金融、通信企業 DevOps 落地實踐案例
Jenkins、Kubernetes、持續交付技術大咖為您分享落地案例
總結
以上是生活随笔為你收集整理的mysql锁历史记录_史上最全MySQL锁机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MTK6577+Android之TP(触
- 下一篇: 思念绵绵,爱在彼此心间漫延