一. 什麼時候使用表鎖 對於INNODB表,在絕大部分情況下都應該使用行鎖。在個別特殊事務中,可以考慮使用表鎖(建議)。 1. 事務需要更新大部份或全部數據,表又比較大,預設的行鎖不僅使這個事務執行效率低,可能造成其他事務長時間鎖等待和鎖衝突,這種情況考慮使用表鎖來提高事務的執行速度(具我在sql ...
一. 什麼時候使用表鎖
對於INNODB表,在絕大部分情況下都應該使用行鎖。在個別特殊事務中,可以考慮使用表鎖(建議)。
1. 事務需要更新大部份或全部數據,表又比較大,預設的行鎖不僅使這個事務執行效率低,可能造成其他事務長時間鎖等待和鎖衝突,這種情況考慮使用表鎖來提高事務的執行速度(具我在sql server中的經歷,該大表有上100w,刪除40w,表鎖有時會造成長時間未執行完成. 還是使用分批來執行好)。
2. 事務涉及多個表,比較複雜,很可能引起死鎖,造成大量事務回滾。這種情況可以考慮一次性鎖定事務涉及的表,避免死鎖,減少資料庫因事務回滾帶來的開銷。
使用表鎖註意兩點
(1) lock tables雖然可以給innodb加表鎖,但表鎖不是由innodb存儲引擎層管理,則是由上層mysql server負責。僅當autocommit=0, innodb_table_locks=1(預設設置)時,innodb層才知道mysql加的表鎖,mysql server也才能感知innodb加的行鎖。
(2) 用lock tables對innodb表加鎖時要註意, 要將autocommit 設置為0,否則mysql 不會給表加鎖; 事務結束前,不要用unlock tables釋放表鎖,因為它會隱式的提交事務。 commit 或rollback 並不能釋放用lock tables 加的表鎖。必須用unlock tables釋放表鎖。
下麵在5.7版本資料庫中,會話2也會阻塞,按上面說法是不會阻塞的,因為會話1沒有設置SET autocommit =0(以後在論證)
-- 會話1 給city加表鎖讀, 不設置 SET autocommit =0 LOCK TABLES city READ
-- 會話2 會阻塞 UPDATE city SET CityCode='005' WHERE city_id=103
-- 會話1提交 COMMIT; -- 會話1 釋放表鎖 UNLOCK TABLES;
二. 關於死鎖
在myisam中是使用的表鎖,在獲得所需的全部鎖時, 要麼全部滿足,要麼等待,因此不會出現死鎖。下麵在innodb中演示一個死鎖例子:
會話1 |
會話2 |
SET autocommit =0 SELECT * FROM city WHERE city_id=103 FOR UPDATE; |
SET autocommit =0 SELECT * FROM cityNew WHERE city_id=103 FOR UPDATE; |
-- 因為會話2 已獲得排他鎖, 該語句等待 SELECT * FROM cityNew WHERE city_id=103 FOR UPDATE; |
|
|
-- 死鎖 SELECT * FROM city WHERE city_id=103 FOR UPDATE; 錯誤代碼: 1213 Deadlock found when trying to get lock; try restarting transaction |
上面案例中, 兩個事務都需要獲得對方持有的排他鎖才能繼續完成事務,這種迴圈鎖等待就是典型的死鎖。 發生死鎖後,innodb會自動檢測到,並使一個事務釋放鎖並回退(回滾),另一個事務得鎖完成事務。
三. 鎖等待查看
涉及外部鎖或表鎖,innodb並不能完全自動檢測到死鎖,這需要設置鎖等待超時參數innodb_lock_wait_timeout來解決(設置需慎重),這個參數並不是只用來解決死鎖問題,在併發下,大量事務無法立即獲得所需鎖而掛起,將占用大量資源,甚至拖跨資料庫 (在sql server中預設是-1 總是等待)。
-- 下麵是5秒 獲取不到鎖就超時 SHOW GLOBAL VARIABLES LIKE 'innodb_lock_wait_timeout';