一个只有35KB的嵌入式数据库,竟藏着让MySQL用户羡慕的功能——代码能自动执行,规则能强制落地,全程无需应用层插手。
这就是SQLite的触发器(Trigger,触发器)。不是新功能,却长期被低估。今天把它聊透。
触发器是什么:数据库的"条件反射"
普通SQL你要手动执行。触发器不同——它绑定在特定事件上,事件一发生,代码自动跑。
三个要素构成每个触发器:
第一,触发时机。BEFORE(操作前拦截)或AFTER(操作后响应)。
第二,触发事件。INSERT、UPDATE、DELETE三种。
第三,触发条件(可选)。WHEN子句判断,条件不满足则静默跳过。
第四,执行动作。真正的SQL逻辑,可以是单条语句,也可以是BEGIN...END包裹的多语句块。
开发者Maneshwar在构建git-lrc时频繁用到这套机制。他的AI代码审查工具需要追踪每次提交,触发器帮他在数据层埋了自动化的钩子——应用代码零侵入。
实战一:余额防负的硬约束
银行账户余额不能为负,这是典型业务规则。放应用层?每个调用方都要重复校验。放数据库?一次定义,全局生效。
Maneshwar给的示例很直接:
CREATE TRIGGER prevent_negative_balance
BEFORE UPDATE OF balance ON accounts
WHEN NEW.balance < 0
BEGIN
SELECT RAISE(ABORT, 'Balance cannot be negative');
逐行拆解:
BEFORE UPDATE OF balance——在修改balance字段之前拦截。
WHEN NEW.balance < 0——检查新值是否为负。NEW指代更新后的行数据。
RAISE(ABORT, ...)——触发异常,整个UPDATE事务回滚。
关键洞察:这条规则对所有访问路径生效。无论是你的主应用、后台脚本、还是某个临时写的Python脚本,想绕过?没门。
这就是数据库层约束的杀伤力——规则与代码解耦,信任边界下沉到存储层。
实战二:审计日志的自动化
敏感字段变更必须留痕,这是合规刚需。传统做法:应用层每个UPDATE后手动插审计表。遗漏风险高,代码冗余重。
触发器方案:
CREATE TRIGGER log_salary_update
AFTER UPDATE OF salary ON employees
BEGIN
INSERT INTO salary_audit(employee_id, old_salary, new_salary, change_date)
VALUES (OLD.id, OLD.salary, NEW.salary, datetime('now'));
这里用AFTER而非BEFORE——审计记录应在变更成功后生成。OLD指代更新前的行数据,与NEW形成前后对比。
datetime('now')是SQLite内置函数,直接取当前时间戳。无需应用层传参。
结果:每次salary字段被修改,审计表自动多一条记录。开发者无感知,运维可追溯。
Maneshwar强调,这类场景是触发器的"甜蜜点"——横切关注点(Cross-cutting Concerns,横切关注点)的自动化处理。日志、审计、校验、级联更新,本就不该散落在业务代码里。
暗面:为什么有人谈触发器色变
功能越强,滥用代价越高。Maneshwar列了三条红线:
第一,隐藏逻辑。触发器不写在应用代码里,新开发者容易忽略。调试时明明SQL执行了,结果却不符合预期——可能是某个触发器在暗中改写。
第二,性能陷阱。高频写入场景,每个INSERT触发复杂计算,吞吐量可能断崖下跌。SQLite虽是嵌入式数据库,但触发器执行仍占事务时间。
第三,调试地狱。多层触发器嵌套(A触发B,B触发C),错误堆栈难以定位。Maneshwar的建议很务实:保持简单、聚焦单一职责、写清楚注释。
一个细节:SQLite触发器不支持INSTEAD OF语法对普通表操作(仅视图支持)。这意味着你无法完全"替换"原操作,只能在前后插入逻辑。这是设计取舍,也是能力边界。
为什么SQLite用户更该关注
PostgreSQL、MySQL都有触发器,但SQLite的场景独特性在于部署形态。
它是单文件、零配置、嵌入应用的。没有独立的DBA团队,没有运维控制台。规则一旦代码化,就是最后的防线。
移动App、IoT设备、浏览器本地存储——这些SQLite的主战场,恰恰是最难事后打补丁的环境。触发器在发布前埋好,比线上事故后救火便宜百倍。
Maneshwar的git-lrc项目选择SQLite,看中的正是这份"自给自足"。AI代码审查需要本地缓存提交历史、规则配置、审查结果,触发器帮他在数据层做了大量自动化,上层只关心业务逻辑。
项目已开源,GitHub可搜git-lrc。Maneshwar的原话:「60秒搭建,完全免费,欢迎反馈和贡献。」
平衡的艺术
触发器不是银弹。Maneshwar的总结很到位:它们是"隐形的守护者",但"像任何强大工具一样,需要纪律"。
我的判断:
在SQLite的场景里,触发器的价值被系统性低估。不是因为功能弱,而是因为心智模型不匹配——开发者习惯在应用层写逻辑,对数据库的"主动性"缺乏直觉。
但边缘计算、本地优先软件(Local-First Software,本地优先软件)的兴起,正在改变这个等式。当应用运行在用户设备上,当网络不可预期,当数据主权回归终端——数据库层的自我管理能力,从"锦上添花"变成"刚需"。
SQLite的触发器,加上它的FTS全文搜索、JSON1扩展、窗口函数,正在组装成一套完整的边缘数据栈。35KB的体积,企业级的武器库。
下一次设计数据模型时,你会把哪些规则下沉到触发器?哪些必须留在应用层保持可见性?这个取舍本身,就是架构能力的试金石。
热门跟贴