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