mysql怎么让一个存储过程定时执行

说实话,MySQL的事件调度器挺有意思的,用起来挺顺手但细节要注意。
我之前在某个电商项目里用过,帮着每天凌晨同步海外库存到本地表。
下面我结合几个踩坑点,具体说说怎么让存储过程定时执行,顺便把文档里那些命令串个线。

先说个我碰过的真实场景:有一次新上线一个定时任务,结果发现表锁死了一整天。
查日志才发现是事件调度器没关,居然在循环执行一个删除操作。
所以第一条建议:用完记得关,别像我一样被老板骂"你咋把数据库搞挂了"。

1 . 开关操作要精准 最直接的是用SET GLOBAL event_scheduler = ON/OFF;。
我习惯在脚本开头加一句SET GLOBAL event_scheduler = ON;,执行完马上ROLLBACK;就自动关了,省得手抖。
但有个地方要注意——如果客户端断开连接,这个全局变量会自动置为OFF。
我在某次Linux定时任务中就遇到过,crontab脚本执行到一半挂了,结果事件全停了。

sql -
开启 mysql> SET GLOBAL event_scheduler = ON; -
查看状态(旧版MySQL用SHOW VARIABLES LIKE '%event%';) mysql> SHOW VARIABLES LIKE '%event_scheduler%'; +-----------------+-------+ | Variable_name | Value | +-----------------+-------+ | event_scheduler | ON | +-----------------+-------+
2 . 存储过程和事件混用要小心 文档里那个CALL user_log_prov();的例子特别典型。
我试过直接在事件里调用存储过程,但有个限制:事件不能处理返回值。
比如你存储过程返回了某条记录的ID,事件日志里只会显示"00000",根本看不了返回值。
后来我改用临时表传数据,才搞通。

sql -
不推荐:存储过程有返回值 CREATE EVENT test_call_proc ON SCHEDULE EVERY 1 HOUR DO CALL my_proc();
-
推荐:用临时表传值 CREATE TEMPORARY TABLE IF NOT EXISTS temp_data(id INT); CREATE EVENT test_call_proc ON SCHEDULE EVERY 1 HOUR DO INSERT INTO temp_data SELECT my_proc();
3 . 关于ON COMPLETION的迷惑 这个选项真的容易混淆。
我一开始也搞不清为啥有些事件用NOT PRESERVE会报错。
后来发现,只有CREATE EVENT时才能指定这个选项,一旦事件创建好,改用ALTER EVENT就没戏了。
比如下面这种写法会报错:
sql -
错误示范 CREATE EVENT e1 ON COMPLETION NOT PRESERVE DO ... -
正确的改写方式 ALTER EVENT e1 ON COMPLETION NOT PRESERVE;
4 . 实际应用中的时间精度问题 文档里那个EVERY 1 SECOND的例子,我试过一次。
结果发现MySQL服务器时间精度不够,会导致每秒插两条重复的记录。
后来改用STARTS '2 02 3 -05 -01 00:00:00' REPEAT EVERY 1 SECOND才正常。
所以定时任务最好设置启动时间而不是周期。

5 . 关键命令的实操建议 我整理个实际操作流程: sql -
先确认调度器状态 SHOW VARIABLES LIKE '%event_scheduler%';
-
创建测试表和存储过程 CREATE TABLE test_log(id INT AUTO_INCREMENT PRIMARY KEY, log_time TIMESTAMP); CREATE PROCEDURE insert_log() BEGIN INSERT INTO test_log(log_time) VALUES(NOW()); END;
-
创建事件(含注释很有用) CREATE EVENT e_test ON SCHEDULE EVERY 1 MINUTE COMMENT '记录操作日志' DO CALL insert_log();
-
立马执行一次看效果 CALL insert_log();
-
查看事件列表 SHOW EVENTS LIKE 'e_test';
-
临时禁用(不推荐,会中断周期) ALTER EVENT e_test DISABLE;
-
停止后马上改回 ALTER EVENT e_test ENABLE;
最逗的是有次改事件周期,写成了EVERY 0 DAY。
结果系统没崩溃,反而每秒执行一次,把数据库CPU干烧了。
所以写脚本要像写代码一样加行级注释,比如:
sql -
错误示范:无限循环事件 CREATE EVENT e_infinite ON SCHEDULE EVERY 0 DAY DO CALL crash_proc(); -
这个存储过程直接死循环
-
正确示范:有终止条件 CREATE EVENT e_daily ON SCHEDULE EVERY 1 DAY DO BEGIN INSERT INTO audit_log VALUES('clean', NOW()); IF (SELECT COUNT() FROM temp_table) > 1 000 THEN TRUNCATE TABLE temp_table; END IF; END;
总之用事件调度器最烦的是调试。
我在某次线上排查时,发现一个创建于凌晨3 点的EVERY 1 DAY事件,居然在上午1 0点才触发——原来是系统时间被运维调过了。
所以创建事件时,最好用AT CURRENT_TIMESTAMP指定绝对时间,避免这类乌龙。

PS:文档里那个CREATE EVENT ... STARTS '2 01 0-00-0000:00:00'是明显笔误,应该是'2 01 0-01 -01 00:00:00'。
这种时候千万别直接用,手动改时间格式可能会触发MySQL的警告机制。

关于mysql存储过程

这SQL语句啊,一看就是想批量导出数据库里的内容,对吧?这思路是挺巧妙的,先用一个存储过程来查找对应ID范围内的数据,然后用bcp命令导出。
不过呢,这个存储过程里有点小问题,我给你指一指。

首先,这createprocmy_search存储过程里,你定义了两个参数@beg_id和@end_id,这是好的。
但是,在while循环里,你把@beg_id的值增加了一个,这样会导致while循环永远执行不完。
你得记得每次循环结束后,@beg_id应该是增加2 ,因为你在查询时已经用@beg_id来定位当前行,而@bcp里又再次使用了@beg_id。

当时我查了一下,这bcp命令挺复杂的,但是大体上是这样的:先查询出对应的记录,然后使用bcp命令把内容导出到文件里。
不过这里有个细节要注意,你的@bcp变量里,路径后面多了一个反斜杠\,这样会导致路径不正确。

另外,这ALTERprocmy_search过程,你加了个路径参数@path,这是为了导出文件到指定路径吧?不过,你在这段代码里对路径的处理有点问题,你先检查了路径最后一个字符是不是反斜杠,如果是,就把路径截断。
但是,这样做可能会导致路径变成空的,这可不行。
你得确保路径是完整的,除非它是以反斜杠结尾。

我估计你是想这样改:
sql ALTER PROCEDURE my_search @beg_id INT, @end_id INT, @path VARCHAR(2 00) AS BEGIN IF (RIGHT(@path, 1 ) = '\') SET @path = LEFT(@path, LEN(@path)
1 )
DECLARE @names VARCHAR(2 00), @bcp VARCHAR(8 000) IF (@beg_id < @end_id) BEGIN WHILE @beg_id <= @end_id BEGIN SELECT @names = subject FROM mytb WHERE tid = @beg_id
SET @bcp = 'bcp "SELECT content FROM cdma.dbo.mytb WHERE tid=' + RTRIM(CONVERT(VARCHAR, @beg_id)) + '" queryout ' + @path + '\' + RTRIM(@names) + '.txt" -c -U sa -P sa' EXEC master..xp_cmdshell @bcp
SET @beg_id = @beg_id + 2 END END END
这改动主要是确保了@beg_id在while循环里每次增加2 ,以及修正了路径处理的问题。
当然,这个存储过程还是有点复杂,实际应用中可能还需要根据具体情况调整。
说实话,写SQL的时候得特别注意这些小细节,不然很容易出错。
我当时也没想明白为什么会出现这样的问题,后来查了查资料才明白。

如何在mysql中排查存储过程执行异常

说白了,排查MySQL存储过程异常就像查错诊病,得结合症状、病历和检查报告一步步来。

先说最重要的两点:错误日志是主线索,异常处理是关键抓手。
去年我们跑那个项目,因为一个小字段类型不匹配直接雪崩效应,用行话说叫雪崩效应,其实就是前面一个小延迟把后面全拖垮了,花了两天才定位到。
另外一点,去年3 000量级并发测试时,死锁4 0001 错误占了一半,但通过开启临时通用查询日志,发现是某个嵌套查询写法把索引搞废了,改了SQL后问题全消失。
还有个细节挺关键的,比如处理主键冲突1 06 2 时,不能只看错误码,得结合当时传入的参数值,去年我们差点把业务逻辑改错。

我一开始也以为异常处理只要DECLARE HANDLER就行,后来发现不对,像死锁重试要加延时,单纯捕获报错会导致事务卡死。
等等,还有个事,调试游标时,千万别忘了检查循环退出条件,去年有个同事把NOT FOUND条件设成ELSE,结果跑出个内存溢出。

建议先看错误日志,确认是不是权限或超时问题,然后加个GET DIAGNOSTICS把错误码和消息打出来,这个点很多人没注意。

MySQL存储过程:它是什么?为什么使用率低?

哎哟,这MySQL存储过程啊,说起来有点复杂,就像一个老式的大衣柜,虽然能放很多东西,但用起来确实有点不方便。
首先呢,维护这个事儿,得数据库管理员和开发者一起动手,这就像两个人抬个东西,一个往上顶,一个往下拽,还得协调好,一不小心就乱套了。
比如,业务逻辑变了,得两边一起改,不然就出错了。

再说说数据库依赖,这就好比说,你买了件衣服,结果发现只能在一家店里穿,换地方就得换衣服,麻烦不麻烦?MySQL的存储过程在Oracle里可能就用不了,得重写,成本高,风险大。

分布式环境里头,存储过程也不太好使,数据分散在好几个地方,一个存储过程想统一操作,那得拆分成好几个,效率低了,网络开销也大了。

调试和测试也不方便,得在数据库里头直接操作,不像写应用层代码那么方便,设置断点、一步步看变量值,这些都不行,问题排查起来费劲。

性能优化也是个问题,虽然能减少SQL调用次数,但遇到复杂查询和高并发,性能可能不如应用层优化,就像你用个老式自行车,想比汽车跑得快,那可不容易。

技术栈限制嘛,现在流行微服务架构和NoSQL数据库,业务逻辑都往应用层和服务层跑,存储过程就显得有点过时了。

学习成本和人才短缺也让人头疼,写存储过程需要懂特定的SQL语法和数据库知识,不像Java、Python那么容易上手,而且会的人不多。

版本控制和协作也是个挑战,存储过程的代码通常放在数据库里,跟应用代码一起版本控制起来挺麻烦的,容易出现冲突或者被忽略。

哎,说到底,这MySQL存储过程啊,用是有用,但得权衡利弊,看具体需求来决定用不用。
就像你买了个老式衣柜,虽然能放东西,但用起来确实不方便,得考虑清楚。