如何同步TRIGGER执行某些行的UPDATE?

时间:2013-08-21 07:08:36

标签: mysql triggers

让我们直截了当。我有应用程序同时在关系数据库中插入新行。在一个多对一关系的端点上,我希望跟踪子行数,并使用触发器进行后续使用。不幸的是,如果新数据包含对同一父行(applicant)的引用,我将获得DEADLOCKS。如何获取更新行的并发锁?以下是我的触发器:

DROP TRIGGER IF EXISTS `incrementEntryCountTrigger`;

DELIMITER $$
CREATE TRIGGER `incrementEntryCountTrigger` AFTER INSERT ON trademark FOR EACH ROW
BEGIN
    UPDATE applicant
        SET entryCount=entryCount+1,
            entryCountChanged=1
        WHERE applicant.id=NEW.applicant_id;
END$$
DELIMITER ;



DROP TRIGGER IF EXISTS `decrementEntryCountTrigger`;
DELIMITER $$
CREATE TRIGGER `decrementEntryCountTrigger` AFTER DELETE ON trademark FOR EACH ROW
    BEGIN
        UPDATE applicant
            SET entryCount=entryCount-1,
                entryCountChanged=1
            WHERE applicant.id=OLD.applicant_id;
    END$$
    DELIMITER ;

trademark

的结构
CREATE TABLE `trademark` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `applicationDate` datetime DEFAULT NULL,
  `applicationNumber` varchar(255) DEFAULT NULL,
  `class` varchar(255) DEFAULT NULL,
  `creationDate` datetime DEFAULT NULL,
  `deleted` tinyint(4) DEFAULT NULL,
  `imageDownloaded` tinyint(4) DEFAULT NULL,
  `modified` datetime DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `registrationDate` datetime DEFAULT NULL,
  `registrationNumber` varchar(255) DEFAULT NULL,
  `trademarkType` varchar(255) DEFAULT NULL,
  `applicant_id` int(11) DEFAULT NULL,
  `country_id` int(11) DEFAULT NULL,
  `service_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniqueApplicationPerServiceContraint` (`applicationNumber`,`service_id`),
  KEY `FK_sv7x27shne6cro3hch7who6vr` (`applicant_id`),
  KEY `FK_4fuuxl1srjn7svpby7rd6j1er` (`country_id`),
  KEY `FK_1g62lp3kjl15f789m7netvlsk` (`service_id`),
  CONSTRAINT `FK_1g62lp3kjl15f789m7netvlsk` FOREIGN KEY (`service_id`) REFERENCES `service` (`id`),
  CONSTRAINT `FK_4fuuxl1srjn7svpby7rd6j1er` FOREIGN KEY (`country_id`) REFERENCES `country` (`id`),
  CONSTRAINT `FK_sv7x27shne6cro3hch7who6vr` FOREIGN KEY (`applicant_id`) REFERENCES `applicant` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2101 DEFAULT CHARSET=utf8

1 个答案:

答案 0 :(得分:3)

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表中,其中applicant_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操作。