sql—递增并返回值的函数(mysql)

vh0rcniy  于 2021-06-17  发布在  Mysql
关注(0)|答案(1)|浏览(497)

假设在表t中有一行id等于1。
如果运行以下sql,则得到的结果等于1(当c列为0时):

SELECT NEXT_ID(1)

但是,如果运行这个命令,结果是1,而不是0(因为表t中没有id=2的行):

SELECT NEXT_ID(2)

下一个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;

我在这里的意图是创建一个计数器,作为一个原子操作递增一个值。那么,为什么在下一个\u id(2)上得到大于0的值?似乎计数器变量已存储在会话中。。。
在多线程应用程序中使用它安全吗?

ie3xauqp

ie3xauqp1#

如果你 DECLARE counter 那你不应该用 @counter 引用变量。在mysql存储函数中,声明的变量没有 @ 西格尔。变量与 @ sigil是用户定义的变量。 @counter 是一个不同的变量 counter ,尽管它们的拼写相同。
在多线程应用程序中这样做安全吗?当然,如果线程通过使用不同的 id 价值观,它们不会冲突(假设 id 是唯一的 t 表)。
即使多个线程使用相同的 id 值,因此需要在 t 表中,将发生的情况是,一个将首先到达那里,在行上获得一个锁,并将其递增。第二个线程将等待获取自己的锁,直到第一个线程提交其事务。然后第二个线程将继续,然后将看到 c .
所以它是安全的,但是它不允许高吞吐量。争用同一线程的线程 id 必须排队等待锁的当前持有者提交其事务。如果您希望多个线程并行工作,您会发现这会成为一个瓶颈。
这就是为什么 AUTO_INCREMENT 存在,因为它会短暂锁定计数器以生成新值,但会立即释放锁,而不是等待调用方的事务完成。这允许并发线程继续工作,而不是互相等待。
你不能模拟 AUTO_INCREMENTUPDATE 操作,这在事务中是必需的。
请回复您的意见:
对不起,我忘了 := 不适用于局部声明的变量。我测试了一下,发现了两种选择:
备选方案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;

我测试了两种方法,它们都有效。

相关问题