【问题标题】:mysql composite primary key - "insert on duplicate key" does not work as expectedmysql 复合主键 - “插入重复键”无法按预期工作
【发布时间】:2015-06-08 14:02:39
【问题描述】:

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();

问题

我预计第二个查询会因为 (name,active) 上的 UNIQUE 约束而失败。奇怪的是它并没有失败。更奇怪的是,它会继续更新主机 1 上实例的 last_active 值。

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

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

问题

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

有什么想法吗?

【问题讨论】:

    标签: mysql


    【解决方案1】:

    ON DUPLICATE KEY UPDATE 子句在重复的PRIMARY KEY 或重复的UNIQUE KEY 上触发。

    如果您指定 ON DUPLICATE KEY UPDATE,并插入一行 将导致 UNIQUE 索引或 PRIMARY KEY、MySQL 中的重复值 对旧行执行 UPDATE。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-21
      • 2016-07-04
      • 1970-01-01
      • 1970-01-01
      • 2013-07-19
      • 1970-01-01
      • 1970-01-01
      • 2019-08-12
      相关资源
      最近更新 更多