【发布时间】:2020-08-17 10:45:22
【问题描述】:
我有一个包含两列的表格:
- caseId,引用外部表列
- caseEventId, int, unique 用于给定的 caseId,我想为同一 caseId 自动递增。
我知道基于另一列的自动增量选项在 mySql 中与 InnoDb 不可用:
MySQL Auto Increment Based on Foreign Key
MySQL second auto increment field based on foreign key
所以我将 caseEventId 生成到触发器中。我的桌子:
CREATE TABLE IF NOT EXISTS mydb.caseEvent (
`caseId` CHAR(20) NOT NULL,
`caseEventId` INT NOT NULL DEFAULT 0,
PRIMARY KEY (`caseId`, `caseEventId`),
# Foreign key definition, not important here.
ENGINE = InnoDB;
还有我的触发器:
CREATE DEFINER=`root`@`%` TRIGGER `mydb`.`caseEvent_BEFORE_INSERT` BEFORE INSERT ON `caseEvent` FOR EACH ROW
BEGIN
SELECT COALESCE((SELECT MAX(caseEventId) + 1 FROM caseEvent WHERE caseId = NEW.caseId),0)
INTO @newCaseEventId;
SET NEW.`caseEventId` = @newCaseEventId;
END
有了这个,我得到了自动递增的 caseEventId。
但是,我需要在我的 INSERT 事务中的进一步调用中重新使用这个新的 caseEventId,所以我将此 id 放入触发器中的 @newCaseEventId 中,并在以下说明中使用它:
START TRANSACTION;
INSERT INTO mydb.caseEvent (caseId) VALUES ('fziNw6muQ20VGYwYPW1b');
SELECT @newCaseEventId;
# Do stuff based on @newCaseEventId
COMMIT;
这似乎工作得很好但是......并发,使用连接池等......?
此@newCaseEventId 变量是否将与使用同一连接的所有客户端共享,当我的客户端服务器启动两个并发事务时我会遇到问题吗?这是在nodejs下使用mysql。
这是安全的,还是有更安全的方法来解决这个问题?谢谢。
编辑 2020/09/24
仅供参考,我完全放弃了这种方法。我试图以不适合使用的方式使用数据库。
基本上我已经删除了 caseEventId,以及任何应该根据给定列值很好地递增的索引。
当我检索数据时,我依靠读取端正确编写的查询来重新创建我的 caseEventId 字段...
【问题讨论】:
-
您通过这样做创建了一个竞争条件。换句话说,如果两个并发会话同时 INSERT 会怎样?它们都将为
max(caseEventId)+1读取相同的值。最终,除了锁定表之外,您无法实现每个caseId的自动增量。 -
@Bill Karwin 这部分是我担心的。如果您有关于如何在不产生潜在死锁的情况下实现表锁的指南,我很感兴趣!
-
LOCK TABLES 会这样做。大多数人会避免这种情况,因为如果您的应用程序需要在表上同时执行高速率的查询,它会严重限制吞吐量。但是,如果您的查询率较低,它可以工作。
-
另一个更常见的解决方案是忘记为每个 caseId 独立编号你的 caseEvent。只需使用自动增量。