MySQL 5.7事件调度器不支持动态表名,无法在DO块中拼接执行如DELETE FROM CONCAT('log_', '202401')的语句;必须通过存储过程配合PREPARE/EXECUTE执行动态SQL,先从information_schema.tables筛选分表名,再构造并安全执行DELETE语句。

为什么不能直接用事件调度器删分表数据
MySQL 5.7 的 EVENT 不支持动态表名,而分表(如 log_202501、log_202502)的表名是按时间变化的。你无法在 CREATE EVENT 的 DO 块里写 DELETE FROM CONCAT('log_', '202401') —— 这会报语法错误。存储过程才是唯一能拼接并执行动态 SQL 的合法方式。
存储过程必须用 PREPARE + EXECUTE 执行动态 DELETE
分表清理的核心是:遍历匹配的表名 → 拼出 DELETE 语句 → 安全执行。这三步只能靠 PREPARE/EXECUTE 完成,且必须在存储过程中封装:
-
SELECT table_name INTO @tbl从information_schema.tables筛出待清理的分表(例如WHERE table_name LIKE 'log\_2024%') SET @sql = CONCAT('DELETE FROM ', @tbl, ' WHERE create_timePREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;- 务必加
LIMIT:避免单次操作锁表太久;5.7 支持DELETE ... LIMIT,但不支持子查询中嵌套LIMIT
调用存储过程前要检查权限和参数安全
存储过程里拼接 SQL 是高危操作,稍有不慎就会被注入或误删。必须做两件事:
- 确保调用用户有
EVENT权限(如果后续要用事件定时调用该存储过程),以及对目标分表的DELETE权限 - 所有用于拼接表名的变量(如
@tbl)必须来自information_schema或白名单校验,禁止接受外部传入的原始字符串。例如:IF @tbl NOT REGEXP '^log_[0-9]{6}$' THEN LEAVE proc_label; END IF; - 不要在存储过程中直接
DROP TABLE—— 分表结构可能被其他逻辑引用;优先DELETE+ 后续OPTIMIZE TABLE
清理后必须手动 OPTIMIZE 回收磁盘空间
这是最容易被忽略的一环:哪怕你用 DELETE ... LIMIT 清掉了几百万行,ibd 文件大小也不会变小,Data_free 仍居高不下。原因很直接 —— InnoDB 只标记空间为“可重用”,不返还给文件系统。
必须在存储过程末尾或定时任务中补上:
SET @opt_sql = CONCAT('OPTIMIZE TABLE ', @tbl);
PREPARE stmt FROM @opt_sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
注意:OPTIMIZE TABLE 是阻塞操作,会锁表,务必安排在业务低峰期;它在 5.7 中等价于 ALTER TABLE ... FORCE,且仅对 InnoDB 和 MyISAM 有效。


















