【问题标题】:How to synchronize TRIGGER execution for UPDATE of certain row?如何为特定行的 UPDATE 同步 TRIGGER 执行?
【发布时间】:2013-08-23 10:35:36
【问题描述】:

让我们直奔主题。我有同时将新行插入关系数据库的应用程序。在一个多对一关系的端点上,我想使用触发器跟踪子行计数以供以后使用。不幸的是,如果新数据包含对同一父行的引用 (applicant),我会遇到死锁。如何获取更新行的并发锁?这是我的触发器:

删除触发器如果存在`incrementEntryCountTrigger`; 分隔符 $$ 在每行插入商标后创建触发器`incrementEntryCountTrigger` 开始 更新申请人 SET entryCount=entryCount+1, entryCountChanged=1 WHERE申请人.id=NEW.applicant_id; 结束$$ 分隔符; 如果存在 `decrementEntryCountTrigger`,则删除触发器; 分隔符 $$ 删除每一行的商标后创建触发器`decrementEntryCountTrigger` 开始 更新申请人 SET entryCount=entryCount-1, entryCountChanged=1 WHERE申请人.id=OLD.applicant_id; 结束$$ 分隔符;

trademark 表的结构

创建表`商标`( `id` int(11) NOT NULL AUTO_INCREMENT, `applicationDate` 日期时间 DEFAULT NULL, `applicationNumber` varchar(255) 默认为空, `class` varchar(255) 默认为空, `creationDate` 日期时间 DEFAULT NULL, `deleted` tinyint(4) 默认为空, `imageDownloaded` tinyint(4) 默认为空, `modified` datetime 默认为 NULL, `name` varchar(255) 默认为空, `registrationDate` 日期时间 DEFAULT NULL, `registrationNumber` varchar(255) 默认为空, `trademarkType` varchar(255) 默认为空, `applicant_id` int(11) 默认为空, `country_id` int(11) 默认为空, `service_id` int(11) 默认为 NULL, 主键(`id`), 唯一键`uniqueApplicationPerServiceContraint`(`applicationNumber`,`service_id`), KEY `FK_sv7x27shne6cro3hch7who6vr`(`applicant_id`), KEY `FK_4fuuxl1srjn7svpby7rd6j1er`(`country_id`), KEY `FK_1g62lp3kjl15f789m7netvlsk` (`service_id`), 约束 `FK_1g62lp3kjl15f789m7netvlsk` 外键 (`service_id`) 参考 `service` (`id`), 约束`FK_4fuuxl1srjn7svpby7rd6j1er`外键(`country_id`)参考`country`(`id`), 约束 `FK_sv7x27shne6cro3hch7who6vr` 外键 (`applicant_id`) 参考 `applicant` (`id`) ) 引擎=InnoDB AUTO_INCREMENT=2101 默认字符集=utf8

【问题讨论】:

  • 请显示这两个表的结构(索引、外键等) - 运行 SHOW CREATE TABLE tablename 并在此处粘贴结果。

标签: mysql triggers


【解决方案1】:
CREATE TABLE `trademark` (    
.......
CONSTRAINT `FK_sv7x27shne6cro3hch7who6vr` FOREIGN KEY (`applicant_id`) 
    REFERENCES `applicant` (`id`)
.....

上面的外键是死锁的来源。

由于外键约束,每次插入trademark 表都会在applicant 表中的相应记录上放置一个共享锁。此锁由 DBMS 放置,以防止其他会话更新/删除行以确保数据库完整性。

想象以下场景:
1. 会话 1 将一条新记录插入到 trademark 表中,申请者 = 2 - 这将共享锁放在 applicant.id = 2
2. 几毫秒后,会话 2 将另一条记录插入到 trademark 表中,申请者 ID = 2 - 这也会在 applicant 表中的相应行上放置一个共享锁。共享锁不冲突,所以此时什么都没有发生。
3. 在会话 1 中,触发了 after insert 触发器 - 触发器正在尝试更新 applicant 表中的行 id=2。由于它被会话 2 锁定(共享锁与写锁冲突) - 因此事务正在等待释放共享锁。
4. 在会话 2 中,触发器 after insert 被触发 - 触发器正在尝试更新同一行。数据库检测到,会话 2 正试图锁定会话 1 试图锁定的同一行,但会话 1 实际上正在等待会话 2 放置的锁 --> 因此 DBMS 报告死锁错误(两个会话正在互相等待)。

你可以做些什么来解决这个问题:
1. 移除外键约束——但这会导致数据完整性问题。
2. 使用命令添加before insert trigger(也可能是before delete):SELECT 1 FROM applicant WHERE applicant.id = NEW.applicant_id FOR UPDATE - 这将在记录上设置写锁并防止死锁,但会减慢所有插入操作。
3. 检测应用程序中的死锁错误并重试 INSERT 操作。

【讨论】:

  • @Antoniossss 这么好的答案,你甚至都不赞成……真可惜。
  • 不幸的是,由于“不允许从触发器返回结果集”错误,我无法在更新/删除触发器之前进行设置。
猜你喜欢
  • 2015-10-17
  • 2015-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多