1. 最開始的理解是 : for update 會對查詢出的結果加行鎖,沒有查詢到結果就不加鎖。
但是今天發現有一句代碼執行for update 卻超時了 。查了mysql 獲取鎖超時時間是 50s .
已我目前業務量不可能有 某一行 被單獨鎖定50s 。 除非是整表有鎖,導致獲取單獨行鎖超時。
排查發現的確是表鎖。
正確鎖行的理解: for update 會對 經過索引(包含主鍵, 此處注意《經過索引》,下面會說明)查詢出的結果行 加行鎖,否則就會加表鎖。
舉例:表 t_log , 主鍵 sys_uuid , 普通索引列 mer_order_no , 普通列 chk_trans_no 。 列類型都是Varchar
sys_uuid | mer_order_no | chk_trans_no |
1 | 1 | 1 |
2 | 2 | 2 |
以下需要開啟兩個命令行,navicat 在tools - console 下。 C1- 命令行1 , C2 - 命令行2
1. C1: start transaction ; select * from t_log where chk_trans_no= 1 for update; (開啟事務不commit)
C2: select * from t_log where chk_trans_no= 2 for update;
發現C2 卡住,C1 commit 后就執行了。 說明 對非索引列使用 for update 是表鎖
2. C1: start transaction ; select * from t_log where mer_order_no = 1 for update;
C2: select * from t_log where mer_order_no = 2 for update;
發現C2 仍然卡住 , mer_order_no 是索引列 啊 , 這是為何?原因在類型Varchar , 數字1 會導致mysql 自動執行
類型轉換,而自動類型轉換索引失效(可以使用 explain sql 看清楚), 所以我上面強調是通過索引查詢出的結果, 而
這時候並不是通過索引查詢出的行,所以加的是表鎖。
把1 和 2 換成 ‘1’ , '2' 。 C2 就不會卡住了,說明是行鎖。
其他說明: 對一張表加行鎖后, 在想加表鎖,需要等待行鎖釋放。
C1: start transaction ; select * from t_log where mer_order_no = ‘1’ for update;
C2: select * from t_log where chk_trans_no= 2 for update; (由於無索引會嘗試加表鎖,等待C1 釋放)
綜上: 請慎用for update , 如果用請配置好索引。
參考: https://www.cnblogs.com/liangge0218/archive/2013/03/26/3292381.html