场景:我有一些触发器可以跟踪一个表的记录数以及其他有用信息。这些触发器在此表上添加/删除/更新时触发,并负责在另一个补充表中写入此信息。
现在这些触发器将在多线程环境中运行,我可能可以同时访问表。 我希望我可以做这样的事情,但它被禁止(错误:错误代码:1314。存储过程中不允许LOCK ):
DELIMITER $$
DROP TRIGGER IF EXISTS del_alarmCount$$
CREATE TRIGGER del_alarmCount AFTER DELETE ON Alarm
FOR EACH ROW
BEGIN
SET autocommit=0;
LOCK TABLES AlarmCount WRITE, AlarmMembership READ;
UPDATE AlarmCount SET num = num - 1
WHERE RuleId = OLD.RuleId AND
MemberId = 0 AND
IsResolved = OLD.IsResolved;
UPDATE AlarmCount SET num = num - 1
WHERE RuleId = OLD.RuleId AND
IsResolved = OLD.IsResolved AND
MemberId IN (SELECT MemberId FROM AlarmMembership WHERE AlarmId=OLD.Id);
COMMIT;
UNLOCK TABLES;
END $$
DELIMITER ;
使用这些LOCKS(或替代构造)实现的目标是:
非常欢迎任何建议!
答案 0 :(得分:9)
我认为处理此问题的最佳方法是使用此处描述的SELECT ... FOR UPDATE模式:http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
供参考:
让我们看另一个例子:我们在a中有一个整数计数器字段 table child_codes,我们用它们为每个人分配一个唯一的标识符 孩子加入了桌子。使用其中任何一个都不是一个好主意 一致读取或共享模式读取以读取当前值 计数器,因为数据库的两个用户可能会看到相同的值 对于计数器,如果两个用户尝试,则会发生重复键错误 将具有相同标识符的子项添加到表中。
这里,LOCK IN SHARE MODE不是一个好的解决方案,因为如果有两个用户 同时读取计数器,其中至少有一个最终进入 尝试更新计数器时死锁。
要实现读取和递增计数器,首先执行a 使用FOR UPDATE锁定计数器的读取,然后递增 计数器。例如:
SELECT counter_field FROM child_codes FOR UPDATE; UPDATE child_codes
SET counter_field = counter_field + 1;
SELECT ... FOR UPDATE读取最新的可用数据,在每行上设置排他锁>它读。因此,它设置搜索的SQL UPDATE将在行上设置的相同锁。
。 。
注意仅使用SELECT FOR UPDATE锁定要更新的行 禁用自动提交时(通过开始事务处理) START TRANSACTION或将autocommit设置为0.如果是自动提交 启用后,与规范匹配的行不会被锁定。
所以在你的情况下,你会替换
LOCK TABLES AlarmCount WRITE, AlarmMembership READ;
UPDATE AlarmCount SET num = num - 1
WHERE RuleId = OLD.RuleId AND
MemberId = 0 AND
IsResolved = OLD.IsResolved;
类似
SELECT num FROM AlarmCount WHERE RuleId = OLD.RuleId AND
MemberId = 0 AND
IsResolved = OLD.IsResolved FOR UPDATE;
UPDATE AlarmCount SET num = num - 1;
我说"类似"因为我并不完全清楚OLD.RuleId和OLD.IsResolved正在引用什么。另外值得注意的是http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html:
前面的描述仅仅是SELECT ... FOR的一个例子 更新工作。在MySQL中,生成唯一的特定任务 标识符实际上只能使用一次访问来完成 表:
UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field +
1);
SELECT LAST_INSERT_ID();
SELECT语句仅检索标识符信息(特定于当前标识符) 连接)。它不访问任何表格。
换句话说,你可以通过只访问一次表来进一步优化这种模式......但是还有一些关于你的模式的细节,我并不完全遵循,而且我也是如此。我不确定我能提供你需要的实际陈述。我确实认为,如果你看看SELECT ... FOR UPDATE,你会看到模式归结为什么,以及你需要做些什么来使你的环境工作。
我还应该提到,您需要考虑一些存储引擎环境和事务隔离级别。关于这个主题的SO有一个非常非常好的讨论:When to use SELECT ... FOR UPDATE?
希望这有帮助!