MySQL存储过程、函数和触发器是开发人员经常打交道的三大术语,但这三个过程可能会对数据库性能造成严重影响,尤其是当函数中出现“死”代码时,另外两个过程将被涉及。下面是笔者进行的一些基础测试,通过对比更好地衡量三者对数据库性能的影响。影响触发器和存储过程性能的一个重要因素是函数“死”代码。尽管所有开发人员都知道一行代码中不应该出现“死”代码(永远不会运行的代码),但可能并不清楚一行“死”代码对性能的影响有多大。“死”代码会显着减慢函数执行、存储过程和触发器的响应时间,因此我们必须仔细清理不必要的代码。下面是一个具体的例子:分析MySQL存储过程函数让我们比较下面四个简单的存储函数(在MySQL5.7中):函数1这个函数只是声明了一个r变量并返回它的值,它是一个虚函数。函数2该函数调用另一个函数levenshtein_limit_n(计算levenshtein距离)。但是,由于IF1=2条件永远不会为真,因此永远不会执行此代码,类似于函数1返回的内容。函数3函数3看起来很复杂,一共有四个条件,但是因为这些条件永远不会成立,所以也是一个“死”的代码调用,结果和函数2和函数1一样。函数4这个函数和函数3一样,selectdoes_not_exit还是不会执行。虽然所有函数返回的r值都是0,但是这四个函数的表现却大不相同。为了衡量性能变化,我们使用基准函数来测试性能。同样运行1M次后,四个函数的性能结果如下:我们可以看到fun1只需要运行1.75秒,fun2有一个死代码调用需要运行2.45秒,而func3(具有四个从未执行的死代码调用)运行速度比func1()慢近3倍,并且func3_nope()具有与func3()完全相同的响应时间。可视化函数的所有系统调用为了弄清楚函数调用期间发生了什么,我们可以使用performance_schema/sys模式通过ps_trace_thread()过程创建跟踪。获取MySQL连接的thread_id:在另一个thread_id=49的连接中运行ps_trace_thread:此时切换到原来的连接(thread_id=49)并运行:sys.ps_trace_thread收集数据(10秒,期间我运行了selectfunc1()),然后它完成收集并创建点文件:我对上面的所有函数重复了这些步骤,然后创建了命令图。结果如下:Func1():Func2():Func3():正如我们所见,每个“if”检查都有一个sp/jump_if_not调用,后面跟一个打开表的语句。因此,解析“IF”条件会对性能产生影响。对于MySQL8.0,我们还可以看到存储过程的MySQL源码文档,里面记录了它的实现,如下:流量分析优化;生成代码后,优化低级sp_instr指令。优化主要集中在两个方面:去死代码;跳捷径。这两个优化是一起进行的,因为它们都涉及到图中流分析的问题,代表生成的代码。实现这些优化的代码是sp_head::optimize()。然而,这并不能解释为什么执行“openingtables”,所以我向官方网站提交了一个错误——MySQLBug#91585。MySQLBug#91585链接:https://bugs.mysql.com/bug.php?id=91585“死”代码如何影响触发器性能?当然,如果我们不执行太多的功能,我们可能不会注意到这些性能差异,但它会在触发器内部产生影响。假设我们在表上有一个触发器,每次更新该表时,它都会执行触发器来更新字段。例如,假设我们有一个名为“form”的表,我们只需要更新它的创建日期:现在我们创建一个触发器来调用我们的虚函数func1():记住:重复更新不会改变新结果,因为我们没有不要在触发器中做任何事情。再添加一个虚拟触发器会使开销加倍,即使触发器不运行任何函数,也会导致同样的减速:现在,让我们使用func3(它有“死”代码,但在功能上等同于func1):如果,我们从触发器内部的函数func3运行代码(而不是调用函数),这将加快更新速度:内存分配即使代码永远不会运行,MySQL仍然需要解析存储过程或触发器代码每次执行,并为它预留足够的内存,这会导致内存泄漏,如MySQLBug#86821中所述。参考链接:https://bugs.mysql.com/bug.php?id=86821结论存储过程和触发器事件在执行时被解析,即使是永远不会运行的“死”代码也会显着影响批量操作性能(例如,在触发器内运行时)。这意味着通过设置“标志”(例如,如果@trigger_disable=0那么...)来禁用触发器仍然会影响批量操作的性能。
