必须使用after触发器同步权限,因before触发器无法保证事务提交和跨表写入原子性;需配套insert/update/delete三类触发器覆盖全生命周期,并严格配置分布式事务与锁机制。

触发器同步权限时,必须用 AFTER UPDATE 而非 BEFORE
BEFORE UPDATE 触发器无法可靠同步权限变更,因为此时事务尚未提交,OLD 和 NEW 值虽可读,但若同步逻辑涉及跨表写入(如更新 user_permissions_sync),可能因锁竞争或未提交状态导致从表写入失败或脏读。AFTER UPDATE 才能确保主表变更已落盘,且触发器内对从表的 UPDATE 或 INSERT 操作在同一个事务上下文中执行,具备原子性。
- MySQL / PostgreSQL 中,
AFTER UPDATE ON users是唯一安全选择;SQL Server 同理,AFTER UPDATE触发器才能访问inserted和deleted伪表 - 若权限字段是 JSON 或逗号分隔字符串(如
roles字段存"admin,editor"),需额外解析——触发器本身不支持 JSON 函数(MySQL 5.7+ 除外),应避免在触发器里做复杂字符串拆分 - 同步目标表(如
user_perms_snapshot)必须有对应字段映射,且主键/唯一约束要与源表一致,否则ON DUPLICATE KEY UPDATE或MERGE会报错
同步 INSERT 和 DELETE 时,别漏掉 deleted 表的关联逻辑
只处理 UPDATE 不足以覆盖全生命周期。用户新建账号需插入权限快照,删除账号则需清理从表冗余记录——这两步必须分别用 AFTER INSERT 和 AFTER DELETE 触发器补全,否则从表会逐渐积压脏数据。
-
AFTER INSERT触发器中,仅能访问NEW(MySQL)或inserted(SQL Server),直接取NEW.id,NEW.role_level等字段写入从表 -
AFTER DELETE触发器中,只能用OLD.id(MySQL)或deleted.id(SQL Server)定位并删除从表对应行,不能依赖 JOIN 查询,否则在高并发下可能误删 - 三个触发器(INSERT/UPDATE/DELETE)命名要有区分,例如
tr_user_perm_ins、tr_user_perm_upd、tr_user_perm_del,便于后续排查和禁用
跨库同步权限时,xact_abort 和 linked server 必须配齐
如果权限主表在 auth_db.users,而从表在另一实例的 app_db.user_perms,光靠触发器语法不够——SQL Server 要求显式启用分布式事务,且必须预配置链接服务器,否则触发器执行到跨库 INSERT 时直接报错 Msg 7391。
- 先执行
sp_addlinkedserver注册目标库,再用sp_addlinkedsrvlogin绑定凭据,这两步缺一不可 - 触发器体内部开头必须加
SET XACT_ABORT ON,否则跨库语句失败时事务不会自动回滚,造成主表更新成功、从表写入失败的不一致状态 - Windows 服务
Distributed Transaction Coordinator必须在两台服务器上都设为“自动启动”,否则即使配置正确,首次同步也会卡住
触发器里不要调用存储过程做权限计算
常见错误是把权限推导逻辑(比如根据用户部门、职级动态生成角色数组)封装进存储过程,再让触发器调用它。这会导致两个问题:一是存储过程执行时间不可控,拖慢主表 DML;二是该过程若含事务控制(BEGIN TRAN),会与触发器所在事务嵌套,引发死锁或 Transaction count after EXECUTE indicates a mismatch 错误。
- 权限字段应尽量前置计算:应用层插入用户时,就把最终生效的
effective_roles值算好,存入主表一个冗余字段,触发器只做直传 - 若必须动态计算,改用视图或物化视图替代触发器同步,或者把同步逻辑移到应用层消息队列(如 Kafka)中异步执行
- MySQL 中尤其注意:触发器内禁止调用含
SELECT ... FOR UPDATE的存储过程,会直接报错Can't update table 'xxx' in stored function/trigger










