让我们直截了当。我有应用程序同时在关系数据库中插入新行。在一个多对一关系的端点上,我希望跟踪子行数,并使用触发器进行后续使用。不幸的是,如果新数据包含对同一父行(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
答案 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操作。