1.1 NoSQL概述与选型错位事故


1.1 NoSQL 概述与选型错位事故

本节摘要:NoSQL 泛指非关系型数据库,分为键值、文档、列族、图四类;MongoDB 属于文档类。本节从一次真实的关系库迁移失败讲起,说明什么时候该选文档库、什么时候不该,并给出可落地的选型判据。

事故档案 01:一场迟到的迁移

2019 年,某电商的埋点日志以 JSON 文本塞在关系库的 VARCHAR 列里。每条日志字段都在变:新版客户端加字段、灰度版本改字段名。半年内 ALTER TABLE 执行了四十多次,每次锁表都造成写入堆积,最终一次大促把库写挂了,事故定级 P1。

复盘结论只有一条:数据结构演进速度远超表结构演进速度,这就是文档数据库要解决的问题。

NoSQL 的四类分支

类别 代表 擅长 不擅长
键值 Redis 极速读写、缓存 复杂查询
文档 MongoDB 灵活结构、二级索引 强关系联结
列族 Cassandra 超大规模写入、多机房 任意维度查询
Neo4j 关系遍历 大范围聚合

文档数据库的核心让步:同一个集合里的文档可以有不同结构。字段加不加、嵌不嵌,由应用层决定,数据库不做强约束(可选的校验见第 5 章)。

什么时候不该选 MongoDB

我更倾向把这些判据写进团队规范,而不是靠个人判断:

  • 数据高度规范化、实体间多对多关系密集、报表依赖大量 JOIN——留在关系库;
  • 需要严格的跨文档约束(外键级联、检查约束)——关系库或用事务模拟(见第 8 章);
  • 只是把 MongoDB 当"更快的 MySQL"用,文档全是平的、单表查询——大概率两头不讨好。
// 埋点日志在 MongoDB 里的自然形态:结构不同也能共存 db.events.insertOne({ ts: ISODate(), userId: "u42", page: "home", os: "ios" }); db.events.insertOne({ ts: ISODate(), userId: "u43", page: "pay", channel: "alipay", amount: 99.5 });

⚠️ 选型错位是所有数据库故障里最贵的一类:它不会报错,只会让成本和复杂度慢慢失控,等发现时已经骑虎难下。

事故复盘:四十次 ALTER 是怎么发生的

值得展开的是定位过程。大促当天写入堆积,第一反应是慢查询,DBA 拿着慢日志翻了两个小时,发现最慢的语句不是业务查询,而是一条排队中的 DDL——灰度版本的新字段又上线了,迁移脚本在等表锁。往前追溯变更记录,半年里这张埋点表被执行了四十多次 ALTER TABLE ADD COLUMN,平均每周两次,每次锁表窗口从几秒涨到几分钟,因为表已经涨到三亿行。数据量、变更频率、结构不稳定三个因素叠在一起,任何关系库都扛不住这种节奏。

修复分两步走。短期先把新字段的写入改为旁路 JSON 列,停止 ALTER;长期把整张表迁到 MongoDB,按天分集合,客户端版本号写入文档,读侧按需兼容。迁移完成后,新字段的上线从"提工单、等窗口、跑 DDL"变成"应用发版即生效",埋点表再没有因为结构变更停过机。

// 迁移后的形态:不同客户端版本的日志结构不同,天然共存 db.events.insertOne({ ts: ISODate("2024-06-01T08:00:00Z"), v: "7.2.1", userId: "u42", page: "home", os: "ios" }); db.events.insertOne({ ts: ISODate("2024-06-01T08:00:01Z"), v: "8.0.0-beta", userId: "u43", page: "pay", channel: "alipay", amount: 99.5, abGroup: "B" }); // 查询侧按需过滤,不同结构互不干扰 db.events.find({ v: /^8\.0/, abGroup: "B" }).count(); // 13742

预防措施只有一条,却最难执行:选型评审必须回答"这份数据的结构未来一年会变几次"。答不上来或者答案是"经常变",就别用需要 DDL 的存储。

与关系库的逐项对照

选型讨论里最容易吵起来,因为双方常在不同维度上各说各话。把争论落到具体能力上会高效很多:

维度 关系库 MongoDB
结构变更 DDL,可能锁表 应用层直接生效
多表联结 JOIN,成熟优化器 聚合管道 lookup,代价高
事务 跨表强事务 单文档原生,多文档 4.0 起支持
校验 列类型、约束、外键 可选 JSON Schema 校验
水平扩展 通常靠中间件分库分表 原生分片,见第 7 章

这张表也解释了为什么"把 MongoDB 当 MySQL 用"两头不讨好:你放弃了 JOIN 和强约束,却又没利用结构灵活与水平扩展,等于只承担了代价没拿到收益。

什么时候该认真考虑 MongoDB

一个具体信号清单:需求文档里出现"字段不固定""不同来源数据格式不一样""日志/轨迹/内容这种只增不改的数据";写入量级在每天千万条以上且持续增长;查询以按键取值和范围扫描为主。三条命中两条,就值得做一次认真的原型验证,用真实数据量压一压,比会议室里争论有效得多。

选型验证的最小压测

信号清单命中之后,别急着上会争论,先花半天做一次最小验证:把生产脱敏后的十万条真实样本灌进测试集合,用应用真实产生的五条最热查询跑一轮,记录 p99 延迟与索引内存占用;再模拟一次加字段发版,体会结构变更的顺手程度。这套动作产出的两个数字,比十页 PPT 更能终结选型争论。

// 最小验证里常被漏掉的一步:确认写入形态真的吃到了文档模型的红利 db.events.insertMany(sampleDocs); // 十万条真实样本 db.events.createIndex({ userId: 1, ts: -1 }); db.events.find({ userId: "u42" }).sort({ ts: -1 }).limit(20).explain("executionStats").executionStats.executionTimeMillis; // 若此处仍是 COLLSCAN 或扫描量巨大,说明查询形态并不适合,信号清单判错了

本节要点回顾

  • NoSQL 四类:键值、文档、列族、图,MongoDB 属文档类;
  • 文档库的让步:结构灵活,换来约束后移到应用层;
  • 选型判据:结构演进快、读多按主键与索引、关系联结少,才考虑 MongoDB;
  • 事故根源:不是关系库不行,是数据形态与数据库假设不匹配。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U