先把当时的情况摆出来。蓝绿发布时给用户表加了一列并设为 NOT NULL,绿环境先跑新代码往里写值。切回蓝环境后旧代码不写这一列,插入直接报 Field 'region_code' doesn't have a default value,MySQL 8.0 开着严格模式。试过把列改成可空,旧代码仍要补默认值才能上线,回滚窗口只有 10 分钟。

把日志和配置对完,能解释这个现象的基本就这几处。DDL 是不对称变更,旧代码不认识新列也没有默认值。也可能是严格模式下缺列直接报错,不会静默插入空值。

少数情况下是应用回滚了但数据库 schema 没有跟着回滚。不容易想到的是,迁移脚本只写了 up 分支,没有可执行的 down。

动手时先确认表里新列的实际定义

mysql -e "SHOW CREATE TABLE users\G" app

再往下把新列临时改成可空并给默认值

mysql -e "ALTER TABLE users MODIFY region_code VARCHAR(16) NULL DEFAULT NULL" app

上线新旧都能写的过渡版本

python -c "print('dual write: old and new column')"

剩下的就是确认空值行数为 0 后再收紧约束

mysql -e "SELECT COUNT(*) FROM users WHERE region_code IS NULL" app

别只看报错消失,验证要多做一步。蓝环境插入恢复正常,绿环境写入的值完整保留,收紧约束前空值行数为 0。

蓝绿共用一份数据库,schema 变更要做成前后兼容的两步,先加可空列再收紧,回滚才不需要动数据。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189