行級鎖定
如果表使用 InnoDB,MySQL 會自動使用行級鎖定,以便多個事務可以同時使用同一個表進行讀寫,而不會讓彼此等待。
如果兩個事務試圖修改同一行並且都使用行級鎖定,則其中一個事務等待另一個事務完成。
也可以通過對預期要修改的每一行使用 SELECT ... FOR UPDATE
語句來獲得行級鎖定。
考慮兩個連線來詳細解釋行級鎖定
連線 1
START TRANSACTION;
SELECT ledgerAmount FROM accDetails WHERE id = 1 FOR UPDATE;
在連線 1 中,通過 SELECT ... FOR UPDATE
語句獲得行級鎖定。
連線 2
UPDATE accDetails SET ledgerAmount = ledgerAmount + 500 WHERE id=1;
當某人嘗試更新連線 2 中的同一行時,將等待連線 1 完成事務或根據 innodb_lock_wait_timeout
設定顯示錯誤訊息,預設為 50 秒。
Error Code: 1205. Lock wait timeout exceeded; try restarting transaction
要檢視有關此鎖的詳細資訊,請執行 SHOW ENGINE INNODB STATUS
---TRANSACTION 1973004, ACTIVE 7 sec updating
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 4, OS thread handle 0x7f996beac700, query id 30 localhost root update
UPDATE accDetails SET ledgerAmount = ledgerAmount + 500 WHERE id=1
------- TRX HAS BEEN WAITING 7 SEC FOR THIS LOCK TO BE GRANTED:
連線 2
UPDATE accDetails SET ledgerAmount = ledgerAmount + 250 WHERE id=2;
1 row(s) affected
但是更新連線 2 中的某些其他行時將執行而不會出現任何錯誤。
連線 1
UPDATE accDetails SET ledgerAmount = ledgerAmount + 750 WHERE id=1;
COMMIT;
1 row(s) affected
現在釋放行鎖,因為事務在連線 1 中提交。
連線 2
UPDATE accDetails SET ledgerAmount = ledgerAmount + 500 WHERE id=1;
1 row(s) affected
通過完成事務,在連線 1 釋放行鎖之後,在連線 2 中執行更新而沒有任何錯誤。