【发布时间】:2018-10-05 17:21:34
【问题描述】:
我正在尝试使用以下 MySQL 表实现锁定机制:
CREATE TABLE Locker (
`Id` int(11),
`locked_until` timestamp null,
unique index `unique` (`Id` asc)
);
这个想法是会有多个进程接收ids 来处理。处理本身发生在 MySQL 之外,但我想使用数据库表来跟踪当前正在进行的处理,并禁止同时处理相同的 id。
我的想法如下:
- 给定
id,我将在表中插入(或更新)相应记录,将locked_until设置为合理值(通常为`CURRENT TIMESTAMP + INTERVAL 30 SECOND) - 在 MySQL 之外进行处理
- 处理项目时,
UPDATE Locker SET locked_until=null WHERE id={current_id}
列表中的第一个项目符号有问题,因为要处理三种不同的情况:
-
id的行不存在 ==> 应该插入 - 一行已存在,但未锁定(
locked_until是null或过去的值)==> 应更新为新的locked_until值 - 一行已经存在,但它已被锁定 ==> 不应在 DB 中执行任何操作,而是向程序发出不应进行处理的信号。
我现在的状态是这样的:
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,但我不确定如何向调用程序发回该元素当前已锁定的信号。 (因为 output 在 SQL Server 中不受支持)
是否可以在一条语句中完成所有这些操作?
更新:
我找到了一种方法,但感觉有点 hack-ish:
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' 值会使其爆炸,表明该特定项目当前已被锁定。
不过,我仍在寻找更好/更清洁的方法。
【问题讨论】:
-
您可以在
current status查询之后检查受影响的行数。对于新的 Id,它将为 1,而对于更新的现有记录,它将返回 2。如果 Id 存在并且locked_until 未更改,则受影响的行数 = 0。至少这是我在旧的 mariadb 实例上看到的。 -
@YuriLachin 问题在于,如果有锁定记录,无论如何都会进行更新。这只是取决于
if的不同值。不幸的是,在DUPLICATE KEY之后我不能UPDATE ... WHERE。 -
如果您
SET locked_until = if(locked_until current_timestamp, current_timestamp + interval 60 second,locked_until),您是否会受到影响的行数> 0? -
@YuriLachin 你是对的!您的解决方案就像一个魅力!您可以将其发布为答案,我会接受。谢谢!
-
@Vatev 你是对的; Yuri 的解决方案效果很好。我犯了一个错误。
标签: mysql