MongoDB 如何做 Schema Validation?
简化版
MongoDB 虽然是灵活 schema,但可以通过 collection validator 做 Schema Validation,常用 $jsonSchema 约束必填字段、类型、枚举和字段范围。它适合守住数据底线,但不能替代完整业务校验和应用层规则。
详细版
Schema Validation 可以在创建集合或 collMod 时配置:
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: { bsonType: "string" },
email: { bsonType: "string" },
age: { bsonType: "int", minimum: 0 }
}
}
}
})
面试里要说清楚:MongoDB 默认不强制同集合文档结构一致,Schema Validation 是可选约束;它能防脏数据,但复杂跨集合、权限、流程状态仍应放在业务层或事务设计里。
完整版教学
一、灵活 schema 不等于没有规则
MongoDB 的优势之一是灵活文档模型。同一个集合里的文档可以字段不同、结构不同,这让需求快速迭代更容易。
但生产系统完全没有规则会很危险。比如有的用户文档叫 email,有的叫 mail,有的 age 是数字,有的是字符串,后续查询、索引、聚合都会痛苦。
Schema Validation 的作用就是在灵活和约束之间取平衡:允许文档模型演进,但关键字段、关键类型、关键枚举要守住。
记忆钩子:MongoDB 不是不要 schema,而是把 schema 从“强制表结构”变成“按需要施加约束”。
二、$jsonSchema 是最常见写法
创建集合时可以指定 validator。下面例子约束用户必须有姓名和邮箱,年龄必须是非负整数。
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: { bsonType: "string" },
email: { bsonType: "string" },
age: { bsonType: "int", minimum: 0 }
}
}
}
})
这里的 bsonType 很关键,因为 MongoDB 底层类型是 BSON,不是普通 JSON 类型。比如整数、长整数、小数在 BSON 里可以区分。
三、validationLevel 和 validationAction 控制严格度
Schema Validation 不只有规则本身,还有执行方式。
| 参数 | 常见值 | 含义 |
|---|---|---|
validationLevel | strict | 对插入和更新都严格检查 |
validationLevel | moderate | 对已有不合规文档更宽松 |
validationAction | error | 不合规则拒绝写入 |
validationAction | warn | 只记录警告,不拒绝 |
这对老集合上线校验很有用。一个已有几千万文档的集合,不一定能立刻切到最严格规则,可以先 warn 观察,再逐步改成 error。
四、Schema Validation 适合守数据底线
适合放进 MongoDB validator 的规则通常是稳定、局部、和单文档有关的规则:
字段必填:name/email/status
类型正确:age 是 int,createdAt 是 date
枚举范围:status in ["NEW", "PAID", "CANCELLED"]
数值边界:amount >= 0
嵌套结构:address.city 是 string
这些规则一旦被破坏,就会产生明显脏数据。放在数据库层可以挡住脚本、后台任务、临时修数等绕过应用服务的写入。
五、它不能替代业务校验
很多规则不适合只靠 Schema Validation。
比如“优惠券只能在活动期内使用”“订单从 PAID 只能流转到 REFUNDING 或 FINISHED”“用户每天最多领取 3 次奖励”,这些规则涉及时间、状态机、跨集合计数或权限。
单文档字段底线 -> Schema Validation
跨集合一致性 -> 事务或业务服务
流程状态流转 -> 应用层状态机
风控权限校验 -> 应用层规则引擎
面试里如果说“MongoDB 有校验,所以业务层不用校验”,就是明显减分点。
六、上线校验要考虑历史数据和灰度
给已有集合加 validator 时,最容易遇到历史脏数据。建议步骤是:
1. 统计现有字段分布和异常类型
2. 清洗或兼容历史数据
3. 先用 warn 观察新写入
4. 修复应用写入路径
5. 切换到 error 严格拒绝
如果直接强上严格校验,可能导致线上写入失败。尤其是多个服务共用一个集合时,要先确认所有写入方都兼容新规则。
七、常见误区与追问
- 误区:MongoDB 是无 schema,所以不能做字段校验。 MongoDB 默认灵活,但可以用 validator 和
$jsonSchema做集合级校验。 - 误区:加了 Schema Validation 就不需要应用校验。 数据库校验守底线,业务校验仍负责流程、权限、跨集合规则。
- 误区:所有字段都应该强制 required。 过度严格会削弱文档模型灵活性,应该约束核心字段。
- 追问:老集合怎么上线校验? 先扫描历史数据,再用
warn观察,修复写入方后切到error。 - 追问:
bsonType和 JSON 类型有什么区别? 它对应 BSON 类型,可以表达日期、整数、小数等更细类型。 - 追问:校验失败会怎样? 取决于
validationAction,error拒绝写入,warn只记录警告。
八、加强记忆
Schema Validation 可以记成“灵活模型加护栏”。MongoDB 默认允许同集合文档结构不同,但生产系统需要用 $jsonSchema 约束核心字段、类型、枚举和数值边界。它适合挡脏数据,不适合承包所有业务规则;上线时要考虑历史数据、多个写入方和灰度策略,通常先观察再严格。