mysql复合主键 - "插入重复键"没有按预期工作

时间:2015-06-08 14:02:39

标签: mysql

TL; DR : SQL小提琴:http://sqlfiddle.com/#!2/0bec3/1 为什么row_was_updated已更新?

背景

我有一个守护程序,它在多个主机上运行多个实例(每个主机一个守护程序实例)以实现冗余。在任何时候,这些实例中只有一个应该是活动实例。所有其他人必须等待活动实例在他们接管“活动”之前死亡。状态。

我有一个MySQL表,用于跟踪所有守护进程实例,并跟踪哪个实例是活动实例。将值 1 插入active列的第一个实例变为活动实例。所有实例都会将当前时间写入last_active列。有一个MySQL触发器可以删除last_active列的值超过30秒的任何行。如果活动实例无法更新其last_active值 - 因为它崩溃了 - 它的行将被数据库删除,另一个实例将成为活动实例。

CREATE TABLE `DT_ActiveCommandModules` (
    `host` INT(11) NOT NULL DEFAULT '0',
    `name` VARCHAR(50) NOT NULL DEFAULT '0' COLLATE 'latin1_general_cs',
    `active` BIT(1) NULL DEFAULT NULL,
    `last_active` TIMESTAMP NULL DEFAULT NULL,
    `row_was_updated` BIT(1) NULL DEFAULT NULL,
    PRIMARY KEY (`host`, `name`),
UNIQUE INDEX `each command module can have only one active instance` (`name`, `active`)
);

想法

每个实例都使用自己的值运行相同的 insert..on重复密钥更新查询。如果查询失败,则实例知道它未成为活动实例,因此必须等待一段时间再试一次。如果查询成功,则实例是活动实例,并且其last_active值已更新。

举个例子,名为 TEST 的守护进程的第一个实例(在主机 1 上)执行此查询(在空表上):

INSERT INTO DT_ActiveCommandModules 
    (host, name, active, last_active, row_was_updated) 
VALUES
    (1,'TEST',1,now(), 0)
ON DUPLICATE KEY UPDATE
    active=1,
    last_active=NOW();

成为活动实例。然后第二个守护进程实例 TEST (在主机 2 上)执行此查询:

INSERT INTO DT_ActiveCommandModules 
    (host, name, active, last_active, row_was_updated) 
VALUES
    (2,'TEST',1,now(), 0)
ON DUPLICATE KEY UPDATE
    active=1,
    last_active=NOW();

问题

我预计第二个查询会因为(名称,活动)上的UNIQUE约束而失败。奇怪的是,它不会失败。更奇怪的是,它会继续更新主机 1 上的实例的last_active值。

所以首先它与唯一键不匹配,但显然它 匹配主键(具有不同的值,host = 1 vs host = 2)???

我创建了这个SQL小提示来显示行为:http://sqlfiddle.com/#!2/0bec3/1 我添加了row_was_updated位以显示来自模拟第二个实例的查询确实更新了第一个实例的行。我真的不明白为什么会这样做。

问题

  1. 为什么第二个查询与唯一键不匹配?
  2. 为什么它与具有不同值的主键匹配?
  3. 如何修复查询以执行我想要的操作? ; - )
  4. 有什么想法吗?

1 个答案:

答案 0 :(得分:0)

重复PRIMARY KEY或重复UNIQUE KEY上的

ON DUPLICATE KEY UPDATE子句触发。

  

如果指定ON DUPLICATE KEY UPDATE,则插入一行   会在UNIQUE索引或PRIMARY KEY,MySQL中导致重复值   执行旧行的更新。

所以这里'TEST', 1匹配唯一约束,与主键的host部分无关,导致子句更新实际上在另一个主机上活动的实例。