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


11

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

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

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

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

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

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


1
将版本控制逻辑推送到存储过程时要记住的一件事-当有人以某种方式绕过审核机制直接写到表时,您会多么卑鄙?
billinkc 2011年

我同意。但另一方面,您可能希望在某些情况下有意绕过该日志记录。当然,这是一个完全不同的问题。我真的只是对性能影响感到好奇。
理查德

Answers:


7

为了简单起见,触发器是实现任何类型的数据库更改跟踪的一种方式。但是,您需要知道使用触发器时幕后发生的事情。

根据MySQL存储过程编程,在第256页的“触发器开销”标题下显示以下内容:

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

触发开销的扩展说明在第529-531页中给出。该部分的重点指出以下内容:

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

在使用触发器时,本书中未提及的另一个因素是:在进行审计日志记录时,请注意将数据记录到什么内容中。我之所以这样说是因为,如果您选择登录到MyISAM表,则在INSERT期间,对MyISAM表的每个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)mysqld从MD转储表结构并将其加载到DM和AU中。

步骤05)转换MD中的所有审核表以使用BLACKHOLE存储引擎

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

步骤07)转换AU中的所有审核表以使用MyISAM存储引擎

完成后

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

这样做是将审核信息存储在单独的数据库服务器上,还可以减少MD通常具有的写入I / O降低。


巨大的答案+++ 1
b_dubb

1

这是批量执行此更新的方法。

对于这个例子

  • table_A具有主键ID
  • 您创建一个名为table_A_Keys2Update的表,其ID仅作为PRIMARY KEY
  • 您使用要更新的table_A中的id填充table_A_Keys2Update

要创建table_A_Keys2Update,请执行以下操作:

CREATE TABLE table_A_Keys2Update SELECT id FROM table_A;
ALTER TABLE table_A_Keys2Update ADD PRIMARY KEY (id);

在用需要递增其修订号的ID填充table_A_Keys2Update之后,执行以下UPDATE JOIN以递增其ID在table_A和table_A_Keys2Update中的所有行的修订号:

UPDATE table_A A INNER JOIN table_A_Keys2Update B USING (id)
SET A.revision = A.revision + 1;

这一行查询可以代替触发器和存储过程。

(可选)您可以将此查询放置在存储过程中,并根据需要调用它。


这真的是我很好奇的插件。如果您插入INSERT INTO审核SELECT <whatever> FROM <primary_table> WHERE <存储过程中的参数>,则可以批量插入。在触发器中,您只需将INSERT INTO审计值<更新后的行中的数据>。那么,单行,逐行的插入会比批量插入快吗?
理查德

为简单起见,触发器会更好:1)提供primary_table在任何高峰时间都不会经历批量插入的情况; 2)需要在任何给定时刻按需读取审核信息,以及3)您的站点人流少。
RolandoMySQLDBA 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.