以下是用例:
我有一个包含许多唯一代码的表,这些代码可用或不可用。作为事务的一部分,我想从表中选择一个可用的代码,然后稍后在事务中更新该行。由于这可能同时发生在同一时间的大量会话中,我希望理想情况下选择一个随机记录并在表上使用行级锁定,以便其他事务不被查询阻止,该查询从中选择一行桌子。
我正在使用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,其中available = 1,然后我在应用程序层中对数组进行随机播放,为订单添加一定程度的随机性。
从tbl_codes中选择id,其中available = 1 limit 50
我开始从循环播放的数组中弹出代码,直到我能够选择一个带锁的代码
从tbl_codes中选择*,其中available = 1且id =:id
答案 0 :(得分:3)
查看MySQL如何实际执行此查询可能很有用:
select * from tbl_codes where available = 1 order by rand() limit 1 for update
这将读取和排序符合WHERE
条件的所有行,使用rand()
生成一个随机数到每行的虚拟列中,根据该行对所有行(在临时表中)进行排序虚拟列,然后从排序集返回到客户端的行,直到达到LIMIT
(在这种情况下只有一个)。 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
希望这有帮助。
答案 1 :(得分:1)
即使没有完全映射到您的问题,这里也会讨论这个问题:http://akinas.com/pages/en/blog/mysql_random_row/
这种方法的问题在于它非常慢。的原因 它是如此之慢,以至于MySQL创建了一个包含所有内容的临时表 结果行并为它们中的每一个分配一个随机排序索引。该 然后对结果进行排序和返回。
这篇文章没有涉及锁。但是,也许MySQL锁定了available = 1
的所有行,并且直到事务结束才释放它们!
那篇文章提出了一些解决方案,其中没有一个似乎对你有好处,除了这个,不幸的是,非常hacky,我没有探究它的正确性。
SELECT * FROM
table
WHERE id> =(SELECT FLOOR(MAX(id)* RAND()) FROMtable
)ORDER BY id LIMIT 1;
这是我能为你做的最好的,因为我没有命令MySQL内部。而且,这篇文章很老了。