递增和返回值的函数(MySQL)

时间:2019-01-03 17:35:00

标签: mysql sql

假设我在表t中有一行ID等于1。

如果运行以下sql,我得到的结果等于1(当列c为0时):

SELECT NEXT_ID(1)

但是,如果运行此命令,我得到的结果是1,而不是0(因为表t中没有ID = 2的行):

SELECT NEXT_ID(2)

NEXT_ID函数:

CREATE FUNCTION NEXT_ID(id INT)
RETURNS VARCHAR(15)
 BEGIN
  DECLARE counter BIGINT DEFAULT 0;
  UPDATE t SET c = (@counter := c +1) WHERE ID = id;
  return @counter;
 END;

我的目的是创建一个计数器,该计数器以原子操作的方式递增一个值。 那么,为什么在NEXT_ID(2)上得到的值大于0?似乎计数器变量已存储在会话中...

在多线程应用程序中使用安全吗?

1 个答案:

答案 0 :(得分:1)

如果您DECLARE counter,则不应使用@counter来引用变量。在MySQL存储函数中,声明的变量没有@符号。标记为@的变量为user-defined variables@counter是与counter不同的变量,即使它们具有相同的拼写。

在多线程应用中这样做安全吗?当然,如果线程通过使用不同的id值来递增不同的行,它们将不会冲突(假设idt表的唯一键)。

即使多个线程使用相同的id值,因此需要增加t表中的同一行,也会发生的事情是首先到达那里,并锁定行,然后增加它。第二个线程将等待获得自己的锁,直到第一个线程提交其事务。然后,第二个线程将继续,然后将看到c的增量值。

所以它很安全,但是它不允许很高的吞吐率。争用同一id的线程将必须排队,并等待锁的当前持有者提交其事务。如果期望多个线程并行执行它们的工作,则会发现这是一个瓶颈。

这就是存在AUTO_INCREMENT的原因,因为它短暂地锁定了计数器以生成新值,但是却立即释放了锁定,而不是等待调用者的事务完成。这样一来,并发线程就可以继续工作,而不必等待对方。

您无法通过AUTO_INCREMENT操作来模拟UPDATE的行为,这些操作必不可少。


发表您的评论

对不起,我忘记了:=不适用于局部声明的变量。我对其进行了测试,发现了两种选择:

替代方法1:不必费心声明局部变量,只需使用用户定义的变量即可。

CREATE FUNCTION NEXT_ID(id INT)
RETURNS VARCHAR(15)
READS SQL DATA
 BEGIN
  UPDATE t SET c = (@counter := c +1) WHERE ID = id;
  RETURN @counter;
 END

替代2:使用局部变量,但将LAST_INSERT_ID()设置为增量值。然后SET将计数器局部变量设为该值。

CREATE FUNCTION NEXT_ID(id INT)
RETURNS VARCHAR(15)
READS SQL DATA
 BEGIN
  DECLARE counter BIGINT DEFAULT 0;
  UPDATE t SET c = LAST_INSERT_ID(c +1) WHERE ID = id;
  SET counter = LAST_INSERT_ID();
  RETURN counter;
 END;

我测试了这两种选择,它们都起作用。