raiserror不会自动回滚事务,因其仅抛出错误而不终止事务流,必须显式执行rollback transaction并配合set xact_abort on才能确保回滚;mysql的before触发器在signal或不可恢复错误时自动回滚主语句,但错误可能被吞需show warnings查看。

SQL Server 触发器里 RAISERROR 为什么没回滚事务
RAISERROR 只是抛出错误,不终止事务流,也不自动回滚——这是最常被误用的点。你写了 RAISERROR('金额超限', 16, 1),INSERT 却照样成功入库,数据已脏。
- 必须紧跟着
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION,否则事务继续挂起 -
RAISERROR后不加RETURN,后续语句仍会执行,可能写入错误数据 - 在
TRY...CATCH块中只RAISERROR而不ROLLBACK,CATCH 退出后事务未清空,下次操作可能报错ERROR 3903(事务未提交或回滚) - 推荐改用
THROW(SQL Server 2012+),它默认中断批处理,但依然要配合SET XACT_ABORT ON才能确保回滚整个事务
MySQL BEFORE 触发器失败是否自动回滚主语句
是的,但仅限于显式失败(如 SIGNAL)或不可恢复错误(外键冲突、字段不存在)。它不是“兜底保险”,而是引擎级原子性保证。
-
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '校验失败'会让主INSERT/UPDATE直接失败并回滚整条语句及其触发逻辑 - 但如果触发器里只是
SELECT一个不存在的列,或除零,错误会被吞掉,客户端无提示——必须立刻执行SHOW WARNINGS查看真实报错 - BEFORE 触发器不能执行
COMMIT或ROLLBACK,否则报错ERROR 1617(Cannot execute statement in a READ ONLY transaction) - 想记录日志又不让主事务失败?得用
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION捕获并写入日志表,而不是靠SIGNAL
触发器里写多张表,为什么锁等待暴涨、TPS 腰斩
触发器天然同步执行,且共享主事务上下文——它把一条 DML 变成 N 次串行写入,锁持有时间直接翻倍。
- MySQL 的
AFTER UPDATE中再UPDATE同一张表会报错Can't update table 't' in stored function/trigger,但通过子查询或INSERT ... SELECT间接更新,可能引发死锁 - SQL Server 触发器内查关联表若没加
NOLOCK或没走索引,会长时间持锁阻塞其他写入 - 高频写入表上定义多个触发器(比如审计 + 同步 + 校验),等于每条 INSERT 都要串行跑三次逻辑,CPU 和 IO 压力陡增
- 真正该做的:把复杂逻辑(如调用外部服务、跨库写入、重试机制)移出触发器,放到应用层或存储过程里统一控制事务边界
autocommit=true 是事务失效的隐形杀手
无论 MySQL 还是 SQL Server,只要连接处于自动提交模式,BEGIN TRANSACTION 和 ROLLBACK 全部失效——你以为在事务里,其实每条语句都在单独提交。
- MySQL 默认
autocommit=1,单条UPDATE执行完立刻落盘,ROLLBACK对它无效 - SQL Server 驱动(如
go-mssqldb)默认autocommit=true,必须在连接字符串里显式加autocommit=false - DDL 语句(
CREATE TABLE、ALTER TABLE)、LOCK TABLES、甚至某些SELECT ... FOR UPDATE都会隐式提交当前事务,让前面的BEGIN形同虚设 - 验证是否真在事务中:
SHOW ENGINE INNODB STATUS(MySQL)查TRANSACTIONS段;SQL Server 查SELECT @@TRANCOUNT是否大于 0
autocommit 开关没关、XACT_ABORT 没设、或 SHOW WARNINGS 没查,都可能让回滚静默失败。











