知识全景图/ 软件工程与系统/ 数据库生产治理/ 如何设计数据库测试、校验与数据正确性护栏

如何设计数据库测试、校验与数据正确性护栏

不要罗列测试类型;先证明这次数据库改动没有把业务语义写坏

这一页只解决一个问题: 数据库设计做完后,怎样证明它真的可靠。不是“接口返回 200”,而是账目不重、余额能平、月报口径不漂、迁移不漏、并发不乱、读模型能对账。
一、事故:测试全绿,生产月报错了

分类从文本字段迁移到 category_id。测试只覆盖“新增账目能选分类”。上线后历史账目有空格、旧分类、停用分类,月报少算一批。脚本成功、接口成功,但数据库语义失败。

你以为测到了其实没测到需要的护栏
接口能新增历史数据能否被正确映射迁移前后行数和异常样本
SQL 能跑月报口径是否仍一致事实源和报表对账
单线程通过重复提交和并发导入是否会写重幂等键、唯一约束、并发测试
结果正确查询计划是否退化EXPLAIN 基线和扫描行数门禁
二、先写业务不变量,不先写测试清单

数据库测试保护的是业务不变量。先写“什么绝不能被破坏”,再决定用单测、集成测试、SQL 校验还是生产巡检。

不变量可执行校验
同一外部流水不能导入两次ledger_id + source + external_txn_id 唯一,并发请求只落一条
转账必须一出一入按 transfer_id 分组,检查 count=2、方向相反、金额相等
作废账目不进入月报月报结果和事实表按同一 status 规则对账
账目不能引用别的账本分类entry、account、category 的 ledger_id 必须一致
余额必须能从流水重算余额快照与流水聚合差异必须为 0 或可解释
-- transfer invariant SELECT transfer_id FROM ledger_entries WHERE transfer_id IS NOT NULL GROUP BY transfer_id HAVING COUNT(*) <> 2 OR SUM(CASE WHEN direction='out' THEN amount_minor ELSE 0 END) <> SUM(CASE WHEN direction='in' THEN amount_minor ELSE 0 END);
三、最小证据包

每次数据库改动上线前,不需要写一份宏大的测试战略。先凑齐下面这个最小证据包。

证据最低要求
影响范围改了哪些表、字段、索引、查询、任务、读模型
不变量校验至少覆盖这次最可能破坏的 3 条业务规则
迁移校验迁移前基线、迁移后差异、异常样本、回填进度
并发校验重复提交、双窗口修改、worker 抢任务至少测一个
查询校验结果样本正确,执行计划没有明显退化
上线巡检发布后检查差异、慢查询、异常分布,并有 owner
不要装全面: 当前阶段最重要的不是测试名词完整,而是这次改动最可能把哪里写坏,你有没有证据证明它没坏。
四、用 6 个样本把护栏跑红一次
故意制造护栏应该怎样反应
插入重复 external_txn_id唯一约束或导入幂等测试失败
只插入一侧转账不变量 SQL 找出 transfer_id
把 posted 改成 voided 后不重算月报事实源和月报差异报警
迁移分类时留下无法映射行迁移门禁阻断切读
删掉关键索引执行计划或扫描行数门禁失败
停掉读模型消费者派生数据延迟和差异巡检发现问题
吸收点: 护栏必须真的能拦一次。永远不变红的测试,可能只是没有测到事故。
五、生产巡检只保留会行动的信号
巡检看什么触发动作
不变量巡检转账不成对、跨账本引用、重复外部流水进入修复队列
差异巡检事实表 vs 月报 / 搜索 / 缓存按范围重建或人工复核
迁移巡检新旧字段差异、回填进度、双写差异不允许旧字段退场
性能巡检慢 SQL、扫描行数、锁等待、连接池水位阻断发布或进入容量治理
六、下一步只按风险下钻
如果风险是重复写和并发乱
如果风险是迁移漏数据
如果风险是读模型错
如果风险是查询变慢
总结: 数据库测试不是测试类型大全,而是一组能证明设计没有写坏业务语义的证据。先写不变量,再造会失败的样本,最后把迁移、并发、读模型、性能和生产巡检接成门禁。