3

这是用例:

我有一张桌子,上面有一堆可用或不可用的唯一代码。作为事务的一部分,我想从表中选择一个可用的代码,然后稍后在事务中更新该行。由于这可能同时发生在许多会话中,我希望理想地选择一个随机记录并在表上使用行级锁定,这样其他事务就不会被从中选择行的查询阻塞桌子。

我使用 InnoDB 作为存储引擎,我的查询看起来像这样:

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

但是,它最终锁定了整个表,而不是只锁定表中的一行。谁能给我一些关于如何做到这一点的指示,以便这个查询不会锁定整个表而只是锁定行?

更新

附录:我能够通过在我的选择中指定一个显式键而不是执行 rand() 来实现行级锁定。当我的查询如下所示:

查询一:

   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,其中可用 = 1,然后我在应用程序层中对数组进行洗牌,以向顺序添加一定程度的随机性。

    从可用的 tbl_codes 中选择 id = 1 限制 50

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

    select * from tbl_codes where available = 1 and id = :id

4

2 回答 2

3

It may be useful to look at how this query is actually executed by MySQL:

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

This will read and sort all rows that match the WHERE condition, generate a random number using rand() into a virtual column for each row, sort all rows (in a temporary table) based on that virtual column, and then return rows to the client from the sorted set until the LIMIT is reached (in this case just one). The FOR UPDATE affects locking done by the entire statement while it is executing, and as such the clause is applied as rows are read within InnoDB, not as they are returned to the client.

Putting aside the obvious performance implications of the above (it's terrible), you're never going to get reasonable locking behavior from it.

Short answer:

  1. Select the row you want, using RAND() or any other strategy you like, in order to find the PRIMARY KEY value of that row. E.g.: SELECT id FROM tbl_codes WHERE available = 1 ORDER BY rand() LIMIT 1
  2. Lock the row you want using its PRIMARY KEY only. E.g.: SELECT * FROM tbl_codes WHERE id = N

Hopefully that helps.

于 2013-01-07T21:27:39.333 回答
1

即使不完全映射到您的问题,这里也讨论了这个问题:http: //akinas.com/pages/en/blog/mysql_random_row/

这种方法的问题是它非常慢。它如此缓慢的原因是 MySQL 创建了一个包含所有结果行的临时表,并为每个结果行分配了一个随机排序索引。然后对结果进行排序并返回。

这篇文章不涉及锁。但是,也许 MySQL 会锁定所有具有的行,available = 1直到事务结束才释放它们!

那篇文章提出了一些解决方案,它们似乎都不适合你,除了这个,不幸的是,非常hacky,我没有探讨它的正确性。

SELECT * FROM tableWHERE id >= (SELECT FLOOR( MAX(id) * RAND()) FROM table) ORDER BY id LIMIT 1;

这是我能为你做的最好的事情,因为我不指挥 MySQL 内部。而且,这篇文章很老了。

于 2013-01-07T21:05:34.160 回答