运维删了一个测试用户,结果订单表少了 12 万行。外键定义是 ON DELETE CASCADE,删除语句只有一行 delete from users where id = 88。第一次遇到会往复杂处想,其实漏掉的多是个小前提。
SELECT COUNT(*) FROM orders WHERE user_id = 88; ALTER TABLE orders DROP FOREIGN KEY fk_orders_user; ALTER TABLE orders ADD CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT;
级联删除由数据库自动执行,业务代码里看不到这条链路,删主表一行会顺着外键把子表相关行全部带走。删除没有事务包裹时连回滚的机会都没有。这一点先确认清楚,后面就不用反复试。
碰到「级联删除」这类问题,我习惯先把当前配置原样备份一份,改坏了立刻换回来,不用现场推。把复现步骤压到三条以内,交给同事也能跑出来,才算真的定位清楚。
改动写完就同步给同事,下一个人踩同一个坑,成本还是你的。
具体做法是,先把外键改成 ON DELETE RESTRICT 让删除直接失败;确需级联的改成软删除 用状态字段标记;执行删除前先跑一遍 select count 看影响范围;生产库删除强制在事务里做 并先备份受影响行。
改完照着核一遍,再删用户时语句直接报 1451 子表数据一条不少。
