对账脚本按 status != 1 捞待处理单,结果比预期少 2 万条。查下来这些行的 status 是 NULL。乍看像缓存或者权限的锅,追下去往往不是。

UPDATE orders SET status = 0 WHERE status IS NULL;
ALTER TABLE orders MODIFY status TINYINT NOT NULL DEFAULT 0;
SELECT COUNT(*) FROM orders WHERE status <> 1 OR status IS NULL;

追下去是这么回事:NULL 表示未知,任何和 NULL 的比较结果都是 NULL,既不是真也不是假,where 里就被过滤掉了。not in 遇到子查询里的 NULL 更彻底,整个条件直接返回空集。

「NULL 比较」相关的验证我通常做两遍,改完一次、隔天再看一次,确认不是临时生效。测试环境和线上的差别,往往就在版本号和那几行被忽略的配置上。

线上出问题先对时间点,跟发布、备份、任务执行的时刻排一排,能省掉一半功夫。日志级别临时调高可以,记得改回去,长期开着既占磁盘又掩盖真问题。

权限不要一口气开到最大,先加到够用再往上试,范围小才好收。

具体做法是,把条件补成 status != 1 or status is null;建列时给状态字段设默认值 并且 not null;存量数据先刷一遍 null 再改约束;用 not exists 替代 not in 避开 null 陷阱。

验收就看一点,对账条数和业务口径一致 新的写入不会再产生 NULL 状态。

作者 9823189