MYSQL触发器导致锁

问题描述 投票:0回答:2

我在 mysql 表上有一个触发器,可以插入到另一个表中(请参阅下面的触发器)。

此触发器会导致另一个表上的锁定,从而阻止同时发生的对同一个表的其他插入。如果我删除触发器,其他插入将按预期工作。

我需要找到一种方法来确保两次插入都会发生。还有另一种方式编写我的触发器会有帮助吗?

更多背景信息:设置此触发器的表是 Joomla 网站扩展的一部分,该扩展是一种将字段插入到此扩展的表中的表单 - 触发器获取一些 new.fields 并将它们插入到 Joomla 核心表中以进行自定义字段(ch54b_fields_values)。作为初始插入的一部分,扩展本身还会将自定义字段写入 Joomla 表中。

如有任何帮助,我们将不胜感激。我不是这方面的专家,所以不想玩弄某些东西并导致其他问题。

IF (new.catid = '131')
THEN
INSERT INTO ch54b_fields_values (field_id, item_id, value) VALUES ('2', new.id, new.created_by);
END IF

我尝试在插入之前和之后执行触发器,结果是相同的。

mysql triggers joomla
2个回答
0
投票

您似乎遇到了由于触发器而导致的表锁定问题。当使用触发器修改另一个表时,这是一个常见问题,因为它可能导致并发访问问题。以下是一些可能有帮助的建议:

  • 使用 SELECT ... FOR UPDATE 模式:此模式用于读取最新的可用数据,在读取的每一行上设置排他锁1。这可以帮助防止两个触发器同时运行并更新相同的记录。

  • 更改存储引擎:如果表的存储引擎是MyISAM,则可能会导致锁定。 MyISAM 使用表级锁定,可以在操作期间锁定整个表。将存储引擎更改为使用行级锁定的 InnoDB 可能会解决问题2。

  • 使用暂存表:另一种解决方案是使用保存待处理更改的“暂存”表。然后,您可以在 MySQL 中创建一个事件以从此暂存表中读取数据并执行所需的更改3。这可以帮助避免触发器引起的循环引用。

  • 重写触发器:您可以考虑重写触发器以更好地处理并发。例如,您可以使用 BEFORE INSERT 触发器来设置要插入到 ch54b_fields_values45 中的值。

以下是如何使用 BEFORE INSERT 方法重写触发器的示例:

CREATE TRIGGER before_insert_on_table
BEFORE INSERT ON your_table
FOR EACH ROW
BEGIN
  IF NEW.catid = '131' THEN
    SET @field_id = '2', @item_id = NEW.id, @value = NEW.created_by;
  END IF;
END;

然后,在表上的插入操作完成后,您可以将值插入

ch54b_fields_values

请注意,这些“只是建议”,您应该彻底测试它们,以确保它们在您的特定用例中按预期工作。


0
投票

我如何知道问题是由锁引起的?
您的答案可能是所有失败的插入都在插入运行时发生。但是:

我的错误是在触发器运行时发生的,是因为表锁,还是同时发生?
这是您需要问自己的一个关键问题。因此,检查 MySQL 日志并查看插入失败的原因以及插入的内容。如果错误消息阐明了您的确切问题,那么您就可以解决它。如果没有,请继续阅读。创建数据库的副本并将其导入到可以自由实验的位置。您对自己的下一个问题:

我的插入是否在我的副本上成功而没有触发我的触发器? (您甚至可以从副本中删除此触发器)
如果插入成功,则很可能是表锁问题。

如果是表锁问题,请考虑切换到 InnoDB(如果您尚未这样做)。此外,可能会失败的插入可以放入事务中并在失败时重试。当然,如果此触发器引起太多麻烦,您可能会考虑将其删除并重新组织应用程序代码中的插入和更新,而不是触发器。您可以创建一个队列,其中包含要在发生冲突的表上完成的更改。

如果插入后来没有成功,那么它不是由于锁而失败,而是由于数据完整性约束等。您可能想要重新插入具有相同唯一值(例如相同的 id 或相同的唯一外键)的相同记录,或者您可能打算插入一条外键引用不再存在的记录的记录。

因此,阅读日志、调试和实验。每个实验都会让您更接近解决方案,而与实验结果无关。

© www.soinside.com 2019 - 2024. All rights reserved.