使用MySql的原子计数器

时间:2019-05-16 09:22:07

标签: mysql transactions

在MySql 8.0中,我正在处理“原子计数器”(在存储过程中),并且 这个简单的解决方法(我无法使用交易)非常适合我的目的:

CREATE PROCEDURE xxx...
   ...
   UPDATE cnt SET value = (@val := value + 1) where id = 1;
   ...

但是当我编译过程时,会收到此警告

Setting user variables within expressions is deprecated and will be removed in a future release. ...

如何避免出现警告消息? (我找不到“新”语法)

2 个答案:

答案 0 :(得分:1)

无需在表达式中设置变量的模拟原子计数器即可

repeat 
  select value + 1 into @value from cnt where id = 1;
  update cnt set value = @value where id = 1 and value = @value - 1;
until (select row_count()) > 0 end repeat;

这实际上不是原子的(因为不同的会话可以增加selectupdate之间的计数器),但是只会在未发生的情况下进行更新,否则会重试(可能无限期地重试)如果您有一个非常繁忙的柜台)。 row_count()用于检查更新是否发生。

该过时的功能没有“新语法”-有意地,在MySQL 9中将无法再执行此操作(因此警告),请参见change log。在表达式中设置用户变量的主要用例是模拟CTE(例如recursive hierarchical queryrank() window-function),并且使用support of CTEs in MySQL 8可以弃用此功能。

对于上下文,语法的预期行为是使

UPDATE cnt SET value = value + 1 where id = 1; 
SELECT value INTO @val from cnt where id = 1; 

具有原子性。

显然,实现此目标的预期方法是使用事务,因此不会像CTE那样需要新的语法来替换您的行为。但是,您可能想检查无法使用事务的原因是否在更新的MySQL版本(具有潜在的新功能)中消失了。

答案 1 :(得分:0)

警告是关于将会话变量设置为UPDATE语句中表达式的副作用。您可以通过将变量分配移到后续的SELECT语句中来避免这种情况。

   START TRANSACTION;
   UPDATE cnt SET value = value + 1 where id = 1;
   SELECT value INTO @val FROM cnt WHERE id =1;
   COMMIT;

如果您在UPDATE之前启动事务并在同一事务中执行SELECT,则不会有争用情况的风险。 UPDATE获取的锁将阻止另一个并发会话在选择该值之前更新同一行。