问题的实质是存什么、怎么查
两者都能存 JSON:文档型数据库的原生文档模型与水平分片更成熟,关系库则以事务、SQL 生态与 JSON 字段加索引见长。选型先看写入形态是整篇文档写入还是多表事务,再看查询是按文档取详情还是多表关联与聚合。
怎么决策更快
已有关系型体系与强事务需求时,先用 JSON 字段承载半结构化数据往往成本更低;文档数量级增长、访问模式以整篇文档为单位时再考虑文档库。反向迁移时联表与约束改造的工作量常被低估,动手前先梳理查询清单。
基本信息
| 数据模型 | 文档库用 BSON 文档模式灵活,关系库是关系表加 JSON 列混合建模 |
|---|---|
| 事务 | 文档库支持多文档事务,关系库是完整 ACID 与服务端逻辑 |
| 索引 | 文档库按文档建多键与复合索引,关系库可对 JSON 字段建表达式索引 |
| 扩展 | 文档库原生分片与副本集,关系库依赖分区表与中间件 |
| 生态 | 文档库驱动与云托管成熟,关系库 SQL 工具与 BI 生态完整 |
同类查询的两种写法
同类查询的两种写法
-- PostgreSQL:对 JSON 字段建表达式索引后按属性过滤
CREATE INDEX idx_orders_attr ON orders USING GIN (attrs);
SELECT id, attrs->>'channel' AS channel
FROM orders
WHERE attrs @> '{"channel": "app"}';
// MongoDB:同一类需求直接在文档内字段上过滤
db.orders.find({ "attrs.channel": "app" })
// 需要跨集合拼装时用 $lookup,注意额外的查询开销
db.orders.aggregate([ { $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "u" } } ])典型使用场景
- 半结构化数据:先用 JSON 字段承载,观察增长后再决定是否拆库
- 高写吞吐的文档流:以文档为单位写入与读取
- 强事务要求:账务、库存一类需要多表一致性的场景
- 检索与分析:把复杂查询交给关系型或检索层处理
- 存量体系兼容:优先复用已有运维与监控能力
关键维度对比
| 维度 | 文档库 | 关系库加 JSON |
|---|---|---|
| 数据模型 | BSON 文档,模式灵活 | 关系表加 JSON 列,混合建模 |
| 事务 | 多文档事务,跨分片有限制 | 完整 ACID 与服务端逻辑 |
| 索引 | 多键与复合索引,按文档建 | JSON 字段可建表达式与通用倒排索引 |
| 扩展 | 原生分片与副本集 | 依赖分区表与中间件 |
| 生态 | 驱动与云托管成熟 | SQL 工具、BI 与迁移生态完整 |
已有关系型体系与强事务需求时,先用 JSON 字段承载半结构化字段通常成本更低;文档数量级增长且访问模式以文档为单位时,再考虑文档库更合适。
在 JSON 字段上做没有索引的查询会退化为全表扫描,高频路径必须建表达式索引;把这种写法当"灵活"的代价是查询计划不可控、数据量上来后无法收拾。