讲解

触发器(TRIGGER)是挂在表上的自动动作:对某表 INSERT、UPDATE、DELETE 之前(BEFORE)或之后(AFTER)自动执行一段 SQL,每影响一行触发一次(FOR EACH ROW)。触发体里用 OLD 引用修改前的行、NEW 引用修改后的行(INSERT 只有 NEW、DELETE 只有 OLD)。经典用途是审计日志——谁改了余额、从多少改成多少,触发器如实记录,应用层想绕都绕不过去;也可做兜底的数据校验和派生字段维护。

事件调度器(EVENT)是数据库内置的 cron:CREATE EVENT 定义一个定时任务,按 EVERY 1 DAY 这样的周期或某个时间点自动执行一段 SQL。它由 event_scheduler 参数控制总开关(8.0 默认开启)。典型用途是周期性维护:清理过期日志、刷新汇总表、定时归档——这些「没人调用也得发生」的任务放事件里,比依赖外部 crontab 更内聚。

两者都要克制使用。触发器的逻辑藏在库里,应用代码看不见:写入突然变慢、数据莫名变化,排查半天才发现是触发器干的——团队里必须文档化。触发体里做重活(调存储过程、写大表)会直接拖慢每一次写入;事件失败只在错误日志里留痕,没有告警的话任务停了都没人知道,关键任务要有外部监控兜底。

示例

审计触发器:accounts 表余额每次变动,自动往 balance_log 写一条旧值/新值记录:

CREATE TABLE accounts (id INT PRIMARY KEY, balance DECIMAL(10,2) NOT NULL);

CREATE TABLE balance_log (
  id INT AUTO_INCREMENT PRIMARY KEY,
  account_id INT NOT NULL,
  old_balance DECIMAL(10,2) NOT NULL,
  new_balance DECIMAL(10,2) NOT NULL,
  changed_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE TRIGGER trg_balance_audit AFTER UPDATE ON accounts
FOR EACH ROW
INSERT INTO balance_log (account_id, old_balance, new_balance) VALUES (OLD.id, OLD.balance, NEW.balance);

INSERT INTO accounts VALUES (1, 1000.00);

UPDATE accounts SET balance = 800.00 WHERE id = 1;

SELECT account_id, old_balance, new_balance FROM balance_log;

定义一个每天清理 30 天前日志的定时事件,并确认调度器开关处于开启状态:

CREATE TABLE op_log (
  id INT AUTO_INCREMENT PRIMARY KEY,
  msg VARCHAR(100),
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE EVENT evt_cleanup_op_log ON SCHEDULE EVERY 1 DAY
DO DELETE FROM op_log WHERE created_at < NOW() - INTERVAL 30 DAY;

SHOW EVENTS\G

SELECT @@GLOBAL.event_scheduler AS scheduler_state;

清理演示用的触发器与事件:

DROP EVENT IF EXISTS evt_cleanup_op_log;

DROP TRIGGER IF EXISTS trg_balance_audit;

常见坑

  • 触发体里做重逻辑:触发器对每行触发一次,里面再更新大表,写入延迟成倍放大,还可能在高峰期拖垮整个库。触发体保持轻量。
  • 触发器连锁触发:A 表的触发器改 B 表,B 表的触发器又改 A 表,形成环路。MySQL 不允许同一语句内的级联循环,会直接报错,但设计时就应避免。
  • 事件没生效却不知道:event_scheduler 被关掉、事件定义有语法错,任务就静默停摆。重要定时任务要有外部监控核对「最后一次执行时间」。
  • 触发器逻辑无人知晓:半年后新人接手,不知道余额变动为什么会写日志表。所有触发器和事件必须登记在文档或迁移脚本里。

小结

触发器按行自动执行,OLD/NEW 引用新旧行,适合审计与兜底校验;事件调度器是库内 cron,适合周期清理;两者都要轻量、要文档化、要监控。下一章处理超大表的手段:分区表。