行級鎖定

如果表使用 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 中執行更新而沒有任何錯誤。