使用数据库

时间:2018-04-25 09:33:34

标签: mysql

我正在尝试使用以下MySQL表实现锁定机制:

CREATE TABLE Locker (
  `Id` int(11), 
  `locked_until` timestamp null,
  unique index `unique` (`Id` asc)
);

我们的想法是会有多个进程接收id来处理。处理本身发生在MySQL之外,但我想使用数据库表来跟踪当前正在进行的处理,并禁止同时处理相同的id

我的想法如下:

  • 给定id我将在表格中插入(或更新)相应的记录,将locked_until设置为合理的值(通常为“CURRENT TIMESTAMP + INTERVAL 30 SECOND”)
  • 在MySQL之外进行处理
  • 处理该项目时,UPDATE Locker SET locked_until=null WHERE id={current_id}

我在列表中的第一个项目符号遇到问题,因为有三种不同的情况需要处理:

  • id ==>不存在行应该插入
  • 一行已存在,但未锁定(locked_untilnull或过去的值)==>应使用新的locked_until
  • 进行更新
  • 一行已存在,但已被锁定==>不应该在数据库中做任何事情,而是向程序发出不应该进行处理的信号。

我目前的状态是:

insert into Locker (Id, locked_until)
values (3, current_timestamp + interval 60 second)
on duplicate key update locked_until = current_timestamp + interval 60 second;

但问题是它忽略了当前更新的行。我无法添加WHERE子句,因为这是无效的语法。

我也会尝试做一个conditional update,但我不确定如何向该调用程序发回该元素当前被锁定的信号。 (因为SQL Server中不支持output

是否可以在一个声明中完成所有这些?

更新

我找到了一种方法,但感觉很糟糕:

insert into Locker (Id, locked_until)
values (3, current_timestamp + interval 60 second)
on duplicate key update locked_until = if(locked_until < current_timestamp,
                                                current_timestamp + interval 60 second,
                                                'error');

如果需要评估if的第二部分,'error'值会使其爆炸,表示该特定项目当前已被锁定。

我仍然在寻找更好/更清洁的方法来做到这一点。

1 个答案:

答案 0 :(得分:1)

您可以在当前状态查询后检查受影响的行数。对于新的Id,它将为1,而对于更新的现有记录,它将返回2.如果Id存在且locked_until未更改,则受影响的行的N = 0.至少这是我在旧的mariadb实例上看到的。