当前位置: 首页 > 科技观察

MySQL存储过程中只读语句超时怎么办?

时间:2023-03-22 15:40:49 科技观察

MySQL有一个参数叫max_execution_time,用来设置只读语句执行的超时时间,但只对单独执行的select语句有效;对于不单独执行的select语句,如包含在存储过程、触发器等中的select语句,内置事务块不生效。官方手册对这个参数的解释如下:max_execution_time应用如下:globalmax_execution_timevalue为新连接的会话值提供默认值。会话值适用于在会话中执行的SELECTexecutions,其中包括noMAX_EXECUTION_TIME(*N*)optimizerhintNisfor0max_execution_time适用于只读SELECT语句。非只读语句是那些调用存储函数的语句,该函数作为副作用修改数据。存储程序中的SELECT语句将忽略max_execution_time。这类不单独出现的select语句如何控制超时时间呢?我们先来看看设置参数max_execution_time的效果。设置该参数后,如果select语句执行时间过长,则直接取消并报错,如下所示:mysql>set@@max_execution_time=1000;QueryOK,0rowsaffected(0.00sec)mysql>selectsleep(2)fromt1limit1;ERROR3024(HY000):查询执行被中断,超过最大语句执行时间或者直接添加Hint来限制select语句的执行时间:下面两种方法可以限制select语句执行时间的作用。mysql>select/*+max_execution_time(1000)*/sleep(2)fromt1limit2;ERROR3024(HY000):查询执行被中断,最大语句执行时间超过mysql>select/*+set_var(max_execution_time=1000)*/sleep(2)fromt1limit2;ERROR3024(HY000):Queryexecutionwasinterrupted,maximumstatementexecutiontimeexceeded如果这个select语句被封装在存储过程中,根据手册中参数max_execution_time的解释是不会生效的。例如,创建一个新的存储过程sp_test:DELIMITER$$USE`ytt`$$DROPPROCEDUREIFEXISTS`sp_test`$$CREATEDEFINER=`admin`@`%`PROCEDURE`sp_test`()BEGINselectsleep(2)从t1限制1;END$$DELIMITER;将max_execution_time的值重新设置为1秒:调用存储过程sp_test,可以正常执行,select语句没有被取消!mysql>调用sp_test;+----------+|睡眠(2)|+----------+|0|+------------+1rowsinset(2.01sec)QueryOK,0rowsaffected(2.01sec)那么如何解决这个问题呢?为了方便大家测试,将语句selectsleep(2)fromt1limit1改为selectsleep(2000)fromt1limit1。既然MySQL层面有这样的限制,我们只能从非MySQL层面寻找解决方案。最直接有效的方法就是写一个脚本主动取消select语句。脚本如下:root@ytt-normal:/home/ytt/script#catkill_query#!/bin/shQUERY_ID=`mysql-ss-e"selectidfrominformation_schema.processlistwhereuser='admin'anddb='ytt'andtime>10andregexp_like(info,'^select','i')"`if[$QUERY_ID];thenecho"killquery$QUERY_ID"mysql-e"killquery$QUERY_ID"把脚本放在后面整理你可以在crontab或者MySQL自带的事件中定时执行。单独执行脚本效果如下:root@ytt-normal:/home/ytt/script#./kill_querykillquery50除了自己写脚本,还有一个工具可以达到类似的效果,就是包含在著名的Percona-toolkit工具箱中,它被称为pt-kill。pt-kill工具可以根据各种触发条件执行指定的动作:如取消指定的SQL语句、杀死指定的会话等。所以可以使用pt-kill工具实现select语句超时自动取消。如下图:pt-kill工具会一直在后台运行,监控MySQL进程,一旦触发条件被激活,就会执行相应的动作。root@ytt-normal:/home/ytt/script#pt-kill--match-db=ytt--match-user=admin--match-host=%\--match-info='^select'--victims=all--busy-time='10s'--print--kill-query#2022-08-15T17:29:03KILLQUERY50(Query11sec)selectsleep(2000)fromt1limit1有点需要注意:select语句超时自动取消的功能不适合生产环境使用!因为你无法预知它执行结果的时效性、上下文是否相关等特性。