行级锁定

如果表使用 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 中执行更新而没有任何错误。