MySQL:如何实现行级事务锁定而不是表锁定

时间:2013-01-07 20:29:54

标签: mysql innodb

以下是用例:

我有一个包含许多唯一代码的表,这些代码可用或不可用。作为事务的一部分,我想从表中选择一个可用的代码,然后稍后在事务中更新该行。由于这可能同时发生在同一时间的大量会话中,我希望理想情况下选择一个随机记录并在表上使用行级锁定,以便其他事务不被查询阻止,该查询从中选择一行桌子。

我正在使用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中存在一些问题,我选择的策略是:

  1. 我选择了50个代码ID,其中available = 1,然后我在应用程序层中对数组进行随机播放,为订单添加一定程度的随机性。

    从tbl_codes中选择id,其中available = 1 limit 50

  2. 我开始从循环播放的数组中弹出代码,直到我能够选择一个带锁的代码

    从tbl_codes中选择*,其中available = 1且id =:id

2 个答案:

答案 0 :(得分:3)

查看MySQL如何实际执行此查询可能很有用:

select * from tbl_codes where available = 1 order by rand() limit 1 for update

这将读取和排序符合WHERE条件的所有行,使用rand()生成一个随机数到每行的虚拟列中,根据该行对所有行(在临时表中)进行排序虚拟列,然后从排序集返回到客户端的行,直到达到LIMIT(在这种情况下只有一个)。 FOR UPDATE会影响整个语句在执行时所执行的锁定,因此当子句在InnoDB 中读取而不是时会应用该子句,因为它们是回到了客户端。

抛开上述明显的性能影响(这很糟糕),你永远不会从中得到合理的锁定行为。

简短回答:

  1. 使用RAND()或您喜欢的任何其他策略选择所需的行,以便找到该行的PRIMARY KEY值。例如:SELECT id FROM tbl_codes WHERE available = 1 ORDER BY rand() LIMIT 1
  2. 仅使用PRIMARY KEY锁定所需的行。例如:SELECT * FROM tbl_codes WHERE id = N
  3. 希望这有帮助。

答案 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())   FROM table)ORDER BY id LIMIT 1;

这是我能为你做的最好的,因为我没有命令MySQL内部。而且,这篇文章很老了。