MySQL MyISAM與表鎖
在數據庫中,除了CPU、內存、IO等的爭用外,數據也是一種供許多用戶共享的資源,如何保證數據併發的一致性、有效性是所有數據庫必須解決的問題,鎖衝突也是影響數據庫併發性能的一個重要因素。MySQL中不同的存儲引擎之間的鎖機制不一定相同,例如MyISAM和MEMORY採用的是表鎖,BDB採用的是頁面鎖,但野支持表鎖,InnoDB默認是行鎖,但也支持表鎖。
MySQL鎖大體分三種:
- 表鎖:開銷小,加鎖快;不會出現死鎖;鎖粒度大,發生鎖衝突機率高,併發度最低
- 行鎖:開銷大,加鎖慢;會出現死鎖;鎖粒度小,發生鎖衝突機率小,併發度最高
- 頁鎖:開銷和加鎖時間介於表鎖和行鎖之間;會出現死鎖,鎖粒度介於表鎖和行鎖之間
僅從鎖的角度來講,表鎖使用於以查詢爲主,只有少量按索引條件更新數據的應用,如WEB應用;而行級鎖更適合於有大量按索引條件併發更新少量不同數據,同時又有併發查詢的應用,如一些在線事務處理(OLTP)系統。
MyISAM表鎖
MyISAM只支持表鎖,這也是早期MySQL的唯一支持的鎖。而隨着應用對事務完整性和併發性要求的不斷提高,MySQL纔開始開發基於事務的存儲引擎,後來慢慢出現了支持頁鎖和BDB和支持行鎖的InnoDB。
表鎖有共享讀鎖和獨佔寫鎖兩種模式。讀鎖與讀鎖是兼容的,讀鎖與寫鎖是互斥的,寫鎖與寫鎖是互斥的。也就是說,MyISAM表的一個連接的讀操作是不會阻塞其它連接的讀操作的,但是會阻塞其它連接的寫操作,而一個連接的寫操作會阻塞其它連接的讀操作和寫操作。
MyISAM在執行查詢(SELECT)時會自動給涉及到的表加上讀鎖,在執行更新(UPDATE、DELETE、INSERT)等,會自動給涉及到的表加上寫鎖。但是我們也可以顯示加鎖/解鎖:
- LOCK TABLES
- tbl_name [[AS] alias] lock_type
- [, tbl_name [[AS] alias] lock_type] ...
- lock_type:
- READ [LOCAL]
- | [LOW_PRIORITY] WRITE
- UNLOCK TABLES
LOCAL關鍵字用於指示允許併發插入,後面會講到
以下演示一些情況鎖定情況:
讀阻塞寫
以下黑背景色的會話A,灰背景色的爲會話B
會話A給user加讀鎖,加鎖後會話A可以查詢,但不可以插入
會話B可以查詢,但插入會阻塞:
會話A釋放鎖後,會話B才插入成功:
只能訪問加鎖的表
對錶加鎖後只能訪問加了鎖的表,不能訪問沒加鎖的表:
凡是用到的別名都要加一次鎖
在使用別名時,必須對別名也加鎖,而且即使是同一個表,如果別名不一樣也需要另外加鎖:
併發插入
MyISAM表的讀和寫是串行的,但在一定條件下,MyISAM表也支持查詢和插入的併發執行,MyISAM引擎有一個concurrent_insert變量,專門用來控制其併發插入的行爲,取值爲0(或NEVER),1(或AUTO)和2(或ALWAYS),當concurrent_insert爲0時,不允許併發插入,當concurrent_insert爲1時如果表中沒有空洞(即表的中間沒有被刪除的行),那麼允許一個會話讀表的同時,另一個表在表尾插入記錄,這是默認值;當concurrent_insert爲2時,允許在表尾併發插入記錄。
接下來演示這三種模式的效果
設置concurrent_insert爲0:
會話A給user表加鎖(注意此處一定要加LOCAL關鍵字)
可以看到,會話B插入會阻塞:
設置concurrent_insert爲1,會話A給user加讀鎖,
會話B依然可以插入,因爲此時表沒有空間碎片(可以用optimize table table_name整理碎片)
刪除一些數據後(製造碎片)再上鎖:
此時會話B再次試圖插入數據,可以看到其會被阻塞:
設置concurrent_insert爲2,會話A給user加讀鎖:
會話B可以在插入數據:
如果你的應用中用了MyISAM的表,並且你想有一定的插入併發,那麼就可以設置concurrent_insert爲2,並且定期地在空閒時期執行OPTIMIZE TABLE來整理碎片。
另外值得注意的是,當一個會話A請求MyISAM表的讀鎖,另一個會話B也請求同一個表的寫鎖時,MySQL會讓會話B獲得寫鎖,甚至,假如鎖等待隊列中讀請求先到,寫請求後到,MySQL同樣也會讓寫請求插入到讀請求前面。這也是爲什麼MyISAM表不太適合於有大量更新操作和查詢操作應用的原因,大量的更新操作會早場查詢操作很難獲得鎖從而可能阻塞很長一段時間。對此,我們可以做一些設置來打破這個規則:
- 通過啓動參數low-priority-updates,使MyISAM引擎默認給予讀請求更高的優先級
- 通過set low-priority-updates = 1,使當前連接發出的所有更新請求的優先級降低
- INSERT、UPDATE、DELETE語句都有一個LOW_PRIORITY屬性,通過該屬性,降低該語句的優先級,例如insert low_priority into user values ...
參考:http://dev.mysql.com/doc/refman/5.7/en/lock-tables.html
深入淺出MySQL