本文目录导读:

在PHP项目中合理使用监听表变更的触发器,通常涉及数据库层面的触发器(Trigger)和应用层面的监听逻辑,下面从几个角度说明如何合理使用。
数据库触发器(MySQL/PostgreSQL)
适用场景
- 数据一致性校验(如:删除用户时自动清理关联数据)
- 自动更新统计表(如:订单新增时更新总数)
- 审计日志记录(每次修改都写入日志表)
示例(MySQL)
-- 创建审计日志表
CREATE TABLE user_audit_log (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT,
action VARCHAR(20),
old_data JSON,
new_data JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建触发器
DELIMITER $$
CREATE TRIGGER after_user_update
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
INSERT INTO user_audit_log (user_id, action, old_data, new_data)
VALUES (NEW.id, 'UPDATE',
JSON_OBJECT('name', OLD.name, 'email', OLD.email),
JSON_OBJECT('name', NEW.name, 'email', NEW.email));
END$$
DELIMITER ;
注意事项
- 性能影响:触发器在事务内执行,频繁操作会降低写入性能
- 调试困难:触发器逻辑对应用层透明,排查问题需要检查数据库
- 避免复杂逻辑:触发器中不要做外部API调用、复杂计算
应用层监听表变更(PHP)
常见方案
定时轮询(简单但低效)
// 每次请求时检查标记
$lastCheck = Cache::get('users_last_update_time');
if ($lastCheck < DB::table('update_log')->max('updated_at')) {
// 表有变更,执行逻辑
}
消息队列 + 数据库钩子
// 在业务代码中发送事件
class UserService {
public function update($id, $data) {
DB::transaction(function() use ($id, $data) {
DB::table('users')->where('id', $id)->update($data);
// 发布变更事件到队列
Redis::publish('table_changes', json_encode([
'table' => 'users',
'action' => 'update',
'id' => $id
]));
});
}
}
// 消费者端监听
$redis->subscribe(['table_changes'], function($message) {
$data = json_decode($message, true);
// 根据表名和操作执行相应逻辑
if ($data['table'] === 'users') {
clearUserCache($data['id']);
}
});
使用Laravel的模型事件(推荐)
// 在模型上定义事件
class User extends Model {
protected static function booted() {
static::updated(function ($user) {
// 自动更新缓存
Cache::forget('user_' . $user->id);
// 同步到搜索引擎
Elasticsearch::index('users', $user->toArray());
});
}
}
混合架构(推荐)
将数据库触发器和应用层逻辑结合,各司其职:
| 层级 | 示例 | |
|---|---|---|
| 数据库触发器 | 校验、审计、级联操作 | AFTER DELETE 清理关联表 |
| 应用层事件 | 缓存刷新、队列任务、外部调用 | 更新后发送通知 |
最佳实践示例
-- 数据库触发器:只做数据级联
CREATE TRIGGER after_user_delete
AFTER DELETE ON users
FOR EACH ROW
BEGIN
DELETE FROM user_roles WHERE user_id = OLD.id;
DELETE FROM user_sessions WHERE user_id = OLD.id;
END;
// 应用层:监听变更并处理业务逻辑
User::deleted(function ($user) {
// 发送邮件通知管理员
Mail::to('admin@example.com')->send(new UserDeleted($user));
// 清除缓存
Cache::forget('user_list');
});
注意事项与权衡
| 点 | 数据库触发器 | 应用层监听 |
|---|---|---|
| 性能 | 低(事务内执行) | 中(取决于实现) |
| 可维护性 | 低(透明、难调试) | 高(代码可见) |
| 可靠性 | 高(事务保证) | 中(可能丢消息) |
| 扩展性 | 低(数据库瓶颈) | 高(可分布式消费) |
建议规则:
- 只使用触发器做数据层的"保险":如约束、级联、必做审计
- 业务逻辑全部放在应用层:缓存、通知、索引更新
- 避免在触发器中调用外部服务:数据库进程出错会影响主操作
- 使用监听队列解耦:变更事件放入Redis/Beanstalkd,PHP进程异步消费
实际案例:订单系统的监听方案
订单表 orders
├─ 数据库触发器:自动更新 order_stats 汇总表(数量/金额)
└─ 应用层:
├─ 模型事件:订单状态变更 -> 发送站内通知
├─ 队列任务:生成PDF发票、更新物流信息
└─ 定时任务:每5分钟检查未处理订单
这种设计既保证了数据一致性(触发器),又使业务逻辑灵活可扩展(应用层)。