首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

MySQL 觸發器 BEFORE vs AFTER 深度對比:防爆庫、資料清洗、審計日誌實戰場景

     編輯:MySQL 綜合教學

MySQL 觸發器 BEFORE vs AFTER 深度對比:防爆庫、資料清洗、審計日誌實戰場景

觸發器執行時機決定了能否修改即將寫入的資料、能否感知寫入結果。BEFORE:寫入前,可攔截修改 NEW 值;AFTER:寫入後,不可修改,可感知生成的自增 ID。本文透過「訂單-庫存」場景深度對比。

核心差異速覽

維度BEFOREAFTER
執行時機儲存引擎寫入資料頁前儲存引擎寫入資料頁後
可修改 NEW✅ SET NEW.col = val;❌ 唯讀
可讀取 OLDUPDATE/DELETE 可用UPDATE/DELETE 可用
可感知自增 ID❌ 尚未生成✅ NEW.id 已有值
事務回滾影響若事務回滾,BEFORE 修改也回滾若事務回滾,AFTER 操作也回滾
典型用途資料清洗、預設值填充、驗證攔截、防爆庫審計日誌、衍生表同步、發送通知、異步佇列寫入

場景實戰:訂單下單扣庫存

表結構

CREATE TABLE goods (
  goods_id INT PRIMARY KEY,
  name VARCHAR(20),
  num SMALLINT  -- 庫存
) ENGINE=InnoDB;

CREATE TABLE `ord` (
  oid INT PRIMARY KEY AUTO_INCREMENT,
  gid INT,
  much INT  -- 下單數量
) ENGINE=InnoDB;

INSERT INTO goods VALUES (1,'cat',26),(2,'dog',26),(3,'pig',26);

❌ AFTER INSERT:爆庫風險

CREATE TRIGGER trg_after_insert
AFTER INSERT ON `ord`
FOR EACH ROW
BEGIN
  UPDATE goods SET num = num - NEW.much WHERE goods_id = NEW.gid;
END$$

問題:訂單已寫入 → 觸發器執行扣減 → 庫存不足時無法阻止訂單建立 → num 變負數(爆庫)。

INSERT INTO `ord` (gid, much) VALUES (2, 30);  -- 庫存只有 26
-- 訂單建立成功,goods.num 變為 -4 ❌

✅ BEFORE INSERT:防爆庫、修正數量

CREATE TRIGGER trg_before_insert
BEFORE INSERT ON `ord`
FOR EACH ROW
BEGIN
  DECLARE stock INT;
  SELECT num INTO stock FROM goods WHERE goods_id = NEW.gid;
  
  IF NEW.much > stock THEN
    -- 策略 1:拋出錯誤阻止下單
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '庫存不足';
    
    -- 策略 2:自動修正為最大可買數量(演示用)
    -- SET NEW.much = stock;
  END IF;
  
  -- 扣減庫存(同一事務內,原子性保證)
  UPDATE goods SET num = num - NEW.much WHERE goods_id = NEW.gid;
END$$

優勢:

  • 可在寫入前檢查庫存,決定是否允許下單
  • 可修正 NEW.much 為合法值
  • 扣減庫存與訂單寫入在同一事務,原子性保證

AFTER INSERT 適用場景:審計日誌

CREATE TRIGGER trg_audit_after_insert
AFTER INSERT ON `ord`
FOR EACH ROW
BEGIN
  INSERT INTO order_audit (order_id, action, qty, created_at)
  VALUES (NEW.oid, 'CREATE', NEW.much, NOW());
END$$

需在 AFTER 才能獲取自增 NEW.oid。

UPDATE/DELETE 時機選擇

操作BEFORE 適用AFTER 適用
UPDATE修正 NEW 值、驗證變更合法性、記錄變更前值 (OLD)同步衍生表、寫審計日誌 (含 NEW/OLD 對比)
DELETE檢查刪除條件、軟刪除標記 (UPDATE 代替 DELETE)清理關聯資料、寫刪除審計日誌

進階技巧:SIGNAL 拋出自定義錯誤

-- BEFORE 觸發器中阻止非法寫入
IF NEW.email NOT LIKE '%@%' THEN
  SIGNAL SQLSTATE '45000' 
  SET MESSAGE_TEXT = '郵箱格式無效', 
      MYSQL_ERRNO = 1001;
END IF;
  • SQLSTATE '45000':通用使用者定義錯誤
  • 應用層捕獲 SQLException 解析錯誤碼/訊息

避坑指南

  • 不要在 AFTER 修改同表資料:會觸發遞歸觸發器,導致無限迴圈或錯誤
  • BEFORE 觸發器中避免耗時操作:阻塞寫入路徑,影響吞吐
  • 觸發器與應用層邊界清晰:核心業務邏輯(扣庫存、積分、優惠計算)留在應用層事務,觸發器僅做輔助(審計、清洗、攔截)
  • 備份/遷移必帶觸發器:mysqldump --triggers 預設包含
選擇時機的黃金法則:需要修改寫入值/阻止寫入 → BEFORE;需要感知寫入結果(自增ID)/不修改源表 → AFTER。現代架構傾向將業務邏輯上移應用層,觸發器退守為「資料完整性最後防線」與「審計日誌自動化」。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved