在查询某个关键字段的时候希望在这个事务修改记录之前其他事务无法读取到这个字段的当前值,只能在事务提交之后才能取得最新值;另外本表的其他行不能被锁住,需要正常访问;毫无疑问加行级锁是最佳选择。但是,行级锁并不是那么简单加锁就能正常使用。在测试的时候会出现了lock wait timeout(超时)和Deadlock found(死锁) 两种错误。使用的是mysql 5.3 innodb数据库。
具体例子如下:
session 1:加锁
mysql> set autocommit=off;
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT * FROM products WHERE id='3' FOR UPDATE;
Query OK, 0 rows affected (0.01 sec)
session 2查询相同的记录,出现等待
mysql> set autocommit=off;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM products WHERE id='3' FOR UPDATE;
mysql> update products set email='test@test.com' where productcat=0 andproductproperty=3;
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4 Changed: 4 Warnings: 0
session 2:
Deadlock found when trying to get lock; try restarting transaction;
原来行级锁锁定的行在后续的程序中只能用加锁时的索引键进行查询更新。如果使用了非索引字段作为查询条件更新的话,将会破坏锁定行的机制,mysql无法判断是否对当前锁定的行产生了更新。由此另一个等待的事务就会收到Deadlock found when trying to get lock; try restarting transaction的错误。可恨的是php程序中开启的事务并不会返回错误,只是返回一个空值,后面的sql可以继续执行。一个关键的锁返回了空值,后续的数据库操作将是毁灭性的。
另外如果等待获取锁的事务超过了一定的时间就会返回lock wait timeout错误,在php程序里同样不会报错,直接返回空查询结果。这个时间是可以由my.ini里的innodb_lock_wait_timeout参数控制的,默认是50秒,可以自己更改。