如何以线程安全的方式查询和增加值(计数器)?(避免比赛条件)


10

在每行有一个计数器(只是一个整数值)的表,我需要的电流值,并增加它在同一时间

实际上,我想这样做:

SELECT counter FROM table WHERE id=123
UPDATE table SET counter=counter+1 WHERE id=123

但是,作为两个查询执行此操作显然不是线程安全的:执行相同操作(在同一行上)的多个进程可能会获得相同的计数器值。我需要它们全部都是唯一的,因此每个过程都将获得实际的当前值并将其增加一个。

我可以想到一种在每行实现一个手动锁的构造,但是我想知道是否有更简单的方法可以做到这一点?


使用交易吗?
ypercubeᵀᴹ

Answers:


15

无需先选择,更新语句就可以正常工作!根据定义,由于单个语句是安全的,因此即使仅同时执行两个UPDATE查询,也将导致行增加两次。

如果您实际上想为您的PHP脚本选择值,请对其进行处理,然后在以后要更新此确切的计数器值,则可以执行以下操作:

BEGIN;
SELECT `counter` FROM `table` WHERE `id` = 123 FOR UPDATE;
UPDATE `table` SET `counter` = `counter`+1 WHERE `id` = 123;
COMMIT;

这将启动一个新事务,然后选择要更新的行并将其排他地锁定。然后,您可以安全地更新它们,而不必担心其他客户端更改其内容,甚至不必访问锁定的行。最后,您需要提交更改。

您还应该阅读一些有关隔离级别的信息。您可能不希望像READ UNCOMMITTED隔离级别这样的值。对于此用例,其他所有内容都应该很好。


我在其他地方读到UPDATE table SET counter = counter + 1足够原子吗?您还需要围绕它的交易记录吗?
CMCDragonkai,2015年

@CMCDragonkai您自己的查询是原子性的,但是如果您选择之前的值并且未使用FOR UPDATE和交易,则您选择的值可能与更新查询中使用的值不同。我的查询组合在选定值后立即锁定了该行,因此确保了此确切的计数器值将在更新查询中使用。
GhostGambler 2015年

好的,但这仅是在我除了增加权利以外要做任何其他工作时才需要的吗?就目前而言,如果仅此而已,那么一个原子更新查询就足够了吗?
CMCDragonkai

1
@CMCDragonkai如果您不执行另一个接触该列的查询,那就太好了。
GhostGambler 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.