1. 数据完整性概述
数据完整性,说白了就是保证你的数据「是对的、没丢、没乱」。
我做了十几年数据架构,见过太多因为数据出问题导致的线上事故。有一次凌晨三点被叫起来,原因是某核心交易表里出现了大量「幽灵记录」——数据凭空多出来,对不上账。嗯,那晚的教训让我深刻理解了:数据完整性不是锦上添花,而是系统的生命线。
什么是数据完整性
数据完整性是指数据的准确性、一致性和可靠性在整个生命周期内得到保证的程度。它包含四个层面:
| 层面 | 说明 | 举个栗子 |
|---|---|---|
| 实体完整性 | 每行数据唯一可识别 | 主键不能为 NULL,不能重复 |
| 域完整性 | 字段值符合定义规则 | 年龄字段不能出现负数 |
| 引用完整性 | 表间关联关系正确 | 订单表的客户 ID 必须在客户表中存在 |
| 用户定义完整性 | 业务规则约束 | 请假结束时间必须大于开始时间 |
你想想看,如果订单表里有个订单指向了不存在的客户,那这个订单还能发货吗?这就是引用完整性被破坏的典型场景。
为什么数据完整性如此重要
我个人习惯把数据完整性比作「地基」。地基歪了,上面的业务逻辑再漂亮也是白搭。
- 决策失准:报表数据不准,管理层拿到的就是错误信息。我见过某公司因为库存数据不一致,多采购了 30% 的原材料,积压资金上千万。
- 系统崩溃:数据不一致可能导致死锁、异常甚至整个服务宕机。
- 合规风险:金融、医疗等行业对数据完整性有硬性监管要求。数据对不上账,轻则罚款,重则吊销牌照。
- 信任崩塌:用户发现自己的订单记录丢了、余额算错了,你觉得他还会再用你的产品吗?
核心观点:数据完整性不是 DBA 一个人的事,它是从研发、测试到运维整个链条共同的责任。谁破坏了完整性,谁就要承担后果——但最终买单的永远是用户和公司。
常见的数据损坏场景
数据损坏不是小概率事件。我这些年踩过的坑,随便列几个:
1. 并发写入冲突
两个线程同时更新同一条记录,没有加锁或使用乐观锁。结果就是「丢失更新」——其中一个写入被覆盖了。
-- 典型的丢失更新场景
-- 事务 A:读取余额 100
-- 事务 B:读取余额 100
-- 事务 A:余额 = 100 - 20 = 80,写入
-- 事务 B:余额 = 100 - 30 = 70,写入(覆盖了 A 的结果)
-- 最终余额 70,实际应该扣 50,但只扣了 30
我曾经在一个支付系统里遇到过这个问题。两个并发请求同时扣款,结果只扣了一次。虽然金额不大,但性质很严重——这属于资金安全漏洞。从那以后,所有涉及金额的操作我都强制要求使用行级锁或乐观锁机制。
2. 硬件故障
磁盘坏道、内存比特翻转、网络丢包……硬件不是 100% 可靠的。你以为数据写进去了,其实可能只写了一半。
- 磁盘静默损坏:磁盘返回写入成功,但实际数据已经损坏。普通文件系统检测不到。
- 内存 ECC 错误:服务器内存如果没开 ECC 校验,一个比特翻转就能让数据库崩溃。
- 网络传输乱序:TCP 虽然保证顺序,但应用层如果没做校验,数据包可能被错误拼接。
3. 软件 Bug
代码里一个不起眼的逻辑错误,可能造成大面积数据污染。
// 一个常见的 Bug:浮点数精度丢失
double price = 0.1;
double quantity = 3;
double total = price * quantity; // 结果是 0.30000000000000004
// 如果直接存到数据库,后续对账就会差那么一点点
小技巧:涉及金额的字段,永远使用 decimal 或整数(以分为单位),别用 float/double。这个坑我帮团队填过不止一次。
4. 人为误操作
别笑,这是最常见的损坏原因。DBA 手滑执行了 DELETE FROM orders 而不是 DELETE FROM orders WHERE id = 123,这种事每天都在发生。
5. 数据迁移/同步异常
从一个库搬到另一个库,或者做主从同步时,如果校验机制不到位,很容易出现数据不一致。
- 主从延迟导致读取到旧数据
- 分片键选择不当导致数据分布不均
- ETL 过程中转换逻辑出错
数据完整性保障体系
说了这么多问题,那怎么解决?我习惯从三个维度来构建防护网:
这个体系图我每次做架构评审都会拿出来讲。预防机制是「别让问题发生」,检测机制是「出了问题能发现」,恢复机制是「发现了能修好」。三者缺一不可。
一句话总结:数据完整性不是某个工具或某个环节能搞定的,它需要从设计、开发、测试到运维全链路去构建。后面的章节,我会带你一步步把这些机制落地。