为什么SQL触发器中不能使用所有聚合函数来维护数据库事务和一致性?
- 内容介绍
- 文章标签
- 相关推荐
本文共计806个文字,预计阅读时间需要4分钟。
简化版开头内容,无需试图解答案,不要数落,不超过100字,直接输出结果:
触发器是行级同步执行,聚合函数却是表级扫描
每次 INSERT/UPDATE/DELETE 一行,触发器就跑一次。如果里面写 SUM()、COUNT() 或子查询套聚合,等于每行都触发一次全表扫描——比如在订单表的 AFTER INSERT 里查 SELECT SUM(amount) FROM orders WHERE user_id = NEW.user_id,1000 个订单插入就要扫 1000 次用户订单记录。
- MySQL 的 InnoDB 在这种场景下极易出现
Lock wait timeout exceeded或Deadlock found when trying to get lock - PostgreSQL 会卡在
SHARE ROW EXCLUSIVE锁上,尤其当聚合查询没走索引时 - 即使加了索引,高并发下多个触发器同时读同一张汇总表,也会争抢
consistent read版本,拖慢主事务
NEW 和 OLD 是快照,聚合结果却要实时算
触发器能直接用 NEW.amount、OLD.status,因为它们是内存里的变更前/后值,零开销。但 SELECT AVG(x) FROM t WHERE y = NEW.y 是实打实去磁盘捞数据,还可能被其他事务正在改的行阻塞。
本文共计806个文字,预计阅读时间需要4分钟。
简化版开头内容,无需试图解答案,不要数落,不超过100字,直接输出结果:
触发器是行级同步执行,聚合函数却是表级扫描
每次 INSERT/UPDATE/DELETE 一行,触发器就跑一次。如果里面写 SUM()、COUNT() 或子查询套聚合,等于每行都触发一次全表扫描——比如在订单表的 AFTER INSERT 里查 SELECT SUM(amount) FROM orders WHERE user_id = NEW.user_id,1000 个订单插入就要扫 1000 次用户订单记录。
- MySQL 的 InnoDB 在这种场景下极易出现
Lock wait timeout exceeded或Deadlock found when trying to get lock - PostgreSQL 会卡在
SHARE ROW EXCLUSIVE锁上,尤其当聚合查询没走索引时 - 即使加了索引,高并发下多个触发器同时读同一张汇总表,也会争抢
consistent read版本,拖慢主事务
NEW 和 OLD 是快照,聚合结果却要实时算
触发器能直接用 NEW.amount、OLD.status,因为它们是内存里的变更前/后值,零开销。但 SELECT AVG(x) FROM t WHERE y = NEW.y 是实打实去磁盘捞数据,还可能被其他事务正在改的行阻塞。

