下面是用例:
我有一张表,上面有一堆唯一的代码,要么是可用的,要么是不可用的。作为事务的一部分,我想从表中选择一个可用的代码,然后稍后在事务中更新该行。由于这种情况可能同时发生在许多会话中,所以理想情况下,我希望选择一个随机记录并对表使用行级锁定,这样其他事务不会被从表中选择行的查询阻止。
我使用innodb作为存储引擎,我的查询如下:
select * from tbl_codes where available = 1 order by rand() limit 1 for update
但是,它不是只锁定表中的一行,而是锁定整个表。有谁能给我一些提示,告诉我如何使查询不锁定整个表,而只锁定行吗?
更新
附录:通过在select中指定显式键而不是rand()来实现行级锁定。当我的问题是这样的:
问题1:
select * from tbl_codes where available = 1 and id=5 limit 1 for update
问题2:
select * from tbl_codes where available = 1 and id=10 limit 1 for update
然而,这并不能真正帮助解决问题。
附录2:最终解决方案
考虑到rand()在mysql中有一些问题,我选择的策略是:
我选择50个代码id的where available=1,然后在应用程序层对数组进行洗牌,以添加一个随机级别。
从TBL U代码中选择ID(如果可用)=1限制50
我开始循环地从无序数组中弹出代码,直到能够选择一个带锁的数组
从tbl_代码中选择*如果可用=1和id=:id
最佳答案
查看mysql如何实际执行此查询可能很有用:
select * from tbl_codes where available = 1 order by rand() limit 1 for update
这将读取并排序符合
WHERE
条件的所有行,使用rand()
为每行生成一个随机数,根据该虚拟列对所有行(在临时表中)排序,然后从排序集返回行到客户端,直到到达LIMIT
为止(在本例中为j只有一个)。FOR UPDATE
影响整个语句在执行时所执行的锁定,因此该子句是作为innodb中的行读取而应用的,而不是作为它们返回给客户机。抛开上述明显的性能影响不谈(这很糟糕),您永远无法从中获得合理的锁定行为。
简短回答:
使用
RAND()
或任何其他策略选择所需的行,以查找该行的PRIMARY KEY
值。例如:SELECT id FROM tbl_codes WHERE available = 1 ORDER BY rand() LIMIT 1
仅使用其
PRIMARY KEY
锁定所需的行。例如:SELECT * FROM tbl_codes WHERE id = N
希望能有所帮助。