MySQL 中触发器与存储过程的性能

Ric*_*ard 11 mysql trigger performance stored-procedures

DBA.StackExchange 上的一篇文章(保持记录修订号的触发器的最佳实践是什么?)引发了一个关于 MySQL 性能的有趣问题(至少对我来说很有趣)。

上下文是我们要为更新的每一行在表中插入一条记录。在更新行之前,我们希望存储先前的值,然后增加其中一列(“版本”列)。

如果我们在触发器中执行此操作,效果会很好。对于 MySQL,触发器是逐行的,因此这将是一个简单的解决方案。选择表中当前的数据,将其插入到日志表中,并在新数据中更新“版本”列。

但是,可以将此逻辑移至存储过程。如果你这样做,你正在执行插入,然后增加表中的“版本”列。整个事情将基于设置。

那么,在执行此插入时,使用基于集合的存储过程方法或基于触发器的方法会更高效吗?

这个问题是针对 MySQL 的(因为它有逐行触发器),尽管它可以应用于其他逐行触发器 DBMS。

Rol*_*DBA 8

为简单起见,触发器是实现任何类型的数据库更改跟踪的方法。但是,您需要了解在使用触发器时会发生什么。

根据MySQL Stored Procedure Programming,“触发器开销”标题下的第 256 页说如下:

重要的是要记住,触发器必然会增加它们所应用的 DML 语句的开销。实际开销将取决于触发器的性质,但是——因为所有 MySQL 触发器都执行 FOR EACH ROW --- 对于处理大量行的语句,开销会迅速累积。因此,您应该避免在触发器中放置任何昂贵的 SQL 语句或过程代码。

第 529-531 页给出了触发器开销的扩展说明。该部分的结论如下:

这里的教训是:由于触发器代码将对受 DML 语句影响的每一行执行一次,触发器很容易成为 DML 性能的最重要因素。触发器主体内的代码需要尽可能轻量级,尤其是触发器中的任何 SQL 语句都应尽可能由索引支持。

书中没有提到使用触发器时的另一个因素:当谈到审计日志时,请注意你将数据记录到什么地方。我这样说是因为如果您选择登录到 MyISAM 表,每个到 MyISAM 表的 INSERT 都会在 INSERT 期间产生一个全表锁。这可能成为高流量、高交易环境中的严重瓶颈。此外,如果触发器针对 InnoDB 表并且您从触发器内部记录 MyISAM 中的更改,这将秘密禁用 ACID 合规性(即,将块事务减少到自动提交行为),这是无法回滚的。

在 InnoDB 表上使用触发器并记录更改时

  • 您登录的表也是 InnoDB
  • 您已关闭自动提交
  • 您彻底设置了 START TRANSACTION...COMMIT/ROLLBACK 块

通过这种方式,审计日志可以像主表一样从 COMMIT/ROLLBACK 中受益。

关于使用存储过程,您必须在 DML 的每个点针对被跟踪的表煞费苦心地调用存储过程。面对数万行应用程序代码,人们很容易错过日志记录更改。将此类代码放置在触发器中可以避免查找所有这些 DML 语句。

警告

根据触发器的复杂程度,它仍然可能是一个瓶颈。如果您想减少审计日志中的瓶颈,您可以采取一些措施。但是,它需要对基础架构进行一些更改。

使用商品硬件,再创建两个数据库服务器

由于审计日志,这将减少主数据库 (MD) 上的写入 I/O。以下是您可以如何完成它:

步骤 01) 在主数据库中打开二进制日志记录。

步骤 02) 使用便宜的服务器,设置 MySQL(与 MD 相同的版本)并启用二进制日志记录。这将是 DM。设置从 MD 到 DM 的复制。

步骤 03) 使用第二台廉价服务器,在禁用二进制日志记录的情况下设置 MySQL(与 MD 相同的版本)。设置每个审计表以使用--replicate-do-table。这将是 AU。设置从 DM 到 AU 的复制。

步骤 04) mysqldump MD 中的表结构并将其加载到 DM 和 AU 中。

Step 05) 将MD中的所有审计表转换为使用BLACKHOLE存储引擎

步骤 06) 将 DM 和 AU 中的所有表转换为使用 BLACKHOLE 存储引擎

Step 07) 将AU中的所有审计表转换为使用MyISAM存储引擎

完成后

  • DM 将从 MD 复制并仅在其二进制日志中记录内容
  • 在所有审计表上使用--replicate-do-table过滤器,AU 将从 DM 复制

这样做是将审计信息存储在单独的数据库服务器上,并减少 MD 通常会出现的任何写入 I/O 降级。