Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/apache-flex/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MySQL:如何实现行级事务锁定而不是表锁定_Mysql_Innodb - Fatal编程技术网

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

MySQL:如何实现行级事务锁定而不是表锁定,mysql,innodb,Mysql,Innodb,以下是用例: 我有一个表,表中有一组唯一的代码,这些代码要么可用,要么不可用。作为事务的一部分,我希望从表中选择一个可用的代码,然后稍后在事务中更新该行。由于这可能会同时发生在许多会话中,因此我希望在理想情况下选择一个随机记录并在表上使用行级锁定,以便其他事务不会被从表中选择行的查询阻止 我使用InnoDB作为存储引擎,我的查询如下: select * from tbl_codes where available = 1 order by rand() limit 1 for update 但

以下是用例:

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

我使用InnoDB作为存储引擎,我的查询如下:

select * from tbl_codes where available = 1 order by rand() limit 1 for update
但是,它并不是只锁定表中的一行,而是锁定整个表。有没有人能给我一些建议,告诉我如何使这个查询不锁定整个表,而只锁定行

更新


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

select * from tbl_codes where available = 1 order by rand() limit 1 for update
问题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:我同意的最终解决方案

鉴于兰德在MySQL中存在一些问题,我选择的策略是:

我在可用的地方选择50个代码id=1,然后在应用程序层中洗牌数组,为顺序添加一定程度的随机性

从可用的tbl_代码中选择id=1限制50

我开始在循环中从我的无序数组中弹出代码,直到我能够选择一个带锁的数组

从可用的tbl_代码中选择*=1和id=:id


即使没有准确地映射到您的问题,这里也会对该问题进行一些讨论:

这种方法的问题是速度很慢。原因 MySQL创建一个临时表,其中包含所有 结果行并为每个行分配一个随机排序索引。这个 然后对结果进行排序并返回

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

那篇文章提出了一些解决方案,其中没有一个似乎对你有好处,除了这一个,不幸的是,它非常粗糙,我没有探究它的正确性

从id>=选择楼层最大id*RAND的表格中选择* 从表中按id限制排序1


这是我能为您做的最好的了,因为我不需要命令MySQL内部。此外,这篇文章非常古老。

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

select * from tbl_codes where available = 1 order by rand() limit 1 for update
这将读取和排序与WHERE条件匹配的所有行,使用rand为每一行生成一个随机数到一个虚拟列中,根据该虚拟列对临时表中的所有行进行排序,然后从排序集将行返回给客户机,直到达到限制(在本例中仅为一行)。FOR UPDATE影响整个语句在执行时执行的锁定,因此,在InnoDB中读取行时应用该子句,而不是在将行返回给客户端时应用该子句

抛开上述明显的性能影响不谈,这很糟糕,您永远无法从中获得合理的锁定行为

简短答复:

使用RAND或任何其他您喜欢的策略选择所需的行,以查找该行的主键值。例如:从可用的tbl_代码中选择id=1按兰德限额订购1 仅使用主键锁定所需的行。例如:从tbl_代码中选择*,其中id=N
希望这会有所帮助。

您确定它最终会锁定整个桌子吗?如何检测?很简单,我打开了两个客户端会话,在每个会话中启动一个事务,在会话1中运行此查询,然后在会话2中运行它,它在会话2中锁定,而不是执行。附录:我能够通过在select中指定显式键而不是执行rand来实现行级锁定。当我的查询如下所示:1。从可用的tbl_代码中选择*更新2的限制1=1和id=5。选择*from tbl_code where available=1,id=10 limit 1进行更新,但这并不能真正帮助解决问题。选择时获得随机性的另一种方法是向查询添加随机偏移量,其中随机偏移量在应用层中生成。例如:从可用的tbl_代码中选择id=1限制:偏移量,50当您有许多代码可供选择时,这很有效。嗯,是的,我怀疑这可能与rand或可用范围有关。在您的解决方案中,查询的问题是,有时它会命中一条不可用的记录,并且根本不返回任何行。我明白了,您是对的。这是我想到的唯一解决办法,但它是昂贵的!是对表进行分区,例如划分可用和不可用,以便黑客可以工作。但我认为这意味着太多的变化。请阅读jeremycole solution with my comm
恩蒂娅,我在想一些类似的事情。。。可能设置范围或类似的东西,比如添加一个类似于id>100和<200的条件,然后错开它们并跟踪哪些范围仍然有可用的id。问题是在点1和点2之间,行可能变得不可用。也许他可以在1和2上循环,直到他可以锁定一个可用的行,2应该成为id=N和available=1的tbl_代码中的SELECT*,即使它看起来不必要。gd1-这正是我想做的,谢谢jeremycole/gd1。