概述
推荐好友列表上线后,MongoDB 在交友项目里已经扛起了推荐、佳人、访客这类社交数据的存储。但到目前为止,所有数据都压在一台 MongoDB 服务器上,这台机器一旦出问题,或者数据涨到单机装不下,业务就直接受影响。
这里说清单机 MongoDB 的两个硬伤、MongoDB 提供的三种集群模式各自解决什么问题,以及项目这种项目到什么量级才需要上集群。
纲要
- 单机 MongoDB 的两个问题:单点故障、海量数据存储
- MongoDB 内置的三种集群方式:主从集群、副本集群、分片集群
- 主从集群为什么被官方淘汰,副本集群如何取代它
- 副本集群与分片集群的定位差异:高可用+读扩展 vs 数据量水平拆分
- 交友项目什么时候需要集群:按数据量级判断
- 常见误解澄清
单机 MongoDB 的两个问题
之前所有章节里,客户端连接的都是一台 MongoDB 服务器,spring.data.mongodb.uri 里也只写了一个地址。这种部署方式有两个绕不开的问题。
第一个问题是单点故障。客户端的所有读写请求都打到这一台机器上,一旦机房断电、磁盘损坏、进程崩溃导致服务器宕机,依赖它的业务立刻不可用,更糟的情况是数据丢失,推荐列表、佳人卡片这些数据没了就是没了。单个节点就是整个链路上的「单点」,它挂了,全局都挂。
第二个问题是海量数据存储。数据虽然落在硬盘上,但硬盘容量是有上限的。假设这台机器只挂了 500GB 的盘,超过 500GB 的数据还能存下吗?加硬盘当然是一种办法,但单机加盘有上限,而且 CPU、内存、网络带宽同样会成为瓶颈,这不是最优解。
用一张图把问题摆出来:
#mermaid-svg-SjtWGhEVnMlfjqVZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SjtWGhEVnMlfjqVZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SjtWGhEVnMlfjqVZ .error-icon{fill:#552222;}#mermaid-svg-SjtWGhEVnMlfjqVZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SjtWGhEVnMlfjqVZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SjtWGhEVnMlfjqVZ .marker.cross{stroke:#333333;}#mermaid-svg-SjtWGhEVnMlfjqVZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SjtWGhEVnMlfjqVZ p{margin:0;}#mermaid-svg-SjtWGhEVnMlfjqVZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SjtWGhEVnMlfjqVZ .cluster-label text{fill:#333;}#mermaid-svg-SjtWGhEVnMlfjqVZ .cluster-label span{color:#333;}#mermaid-svg-SjtWGhEVnMlfjqVZ .cluster-label span p{background-color:transparent;}#mermaid-svg-SjtWGhEVnMlfjqVZ .label text,#mermaid-svg-SjtWGhEVnMlfjqVZ span{fill:#333;color:#333;}#mermaid-svg-SjtWGhEVnMlfjqVZ .node rect,#mermaid-svg-SjtWGhEVnMlfjqVZ .node circle,#mermaid-svg-SjtWGhEVnMlfjqVZ .node ellipse,#mermaid-svg-SjtWGhEVnMlfjqVZ .node polygon,#mermaid-svg-SjtWGhEVnMlfjqVZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SjtWGhEVnMlfjqVZ .rough-node .label text,#mermaid-svg-SjtWGhEVnMlfjqVZ .node .label text,#mermaid-svg-SjtWGhEVnMlfjqVZ .image-shape .label,#mermaid-svg-SjtWGhEVnMlfjqVZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-SjtWGhEVnMlfjqVZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SjtWGhEVnMlfjqVZ .rough-node .label,#mermaid-svg-SjtWGhEVnMlfjqVZ .node .label,#mermaid-svg-SjtWGhEVnMlfjqVZ .image-shape .label,#mermaid-svg-SjtWGhEVnMlfjqVZ .icon-shape .label{text-align:center;}#mermaid-svg-SjtWGhEVnMlfjqVZ .node.clickable{cursor:pointer;}#mermaid-svg-SjtWGhEVnMlfjqVZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SjtWGhEVnMlfjqVZ .arrowheadPath{fill:#333333;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SjtWGhEVnMlfjqVZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SjtWGhEVnMlfjqVZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SjtWGhEVnMlfjqVZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SjtWGhEVnMlfjqVZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SjtWGhEVnMlfjqVZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SjtWGhEVnMlfjqVZ .cluster text{fill:#333;}#mermaid-svg-SjtWGhEVnMlfjqVZ .cluster span{color:#333;}#mermaid-svg-SjtWGhEVnMlfjqVZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SjtWGhEVnMlfjqVZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SjtWGhEVnMlfjqVZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-SjtWGhEVnMlfjqVZ .icon-shape,#mermaid-svg-SjtWGhEVnMlfjqVZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SjtWGhEVnMlfjqVZ .icon-shape p,#mermaid-svg-SjtWGhEVnMlfjqVZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SjtWGhEVnMlfjqVZ .icon-shape .label rect,#mermaid-svg-SjtWGhEVnMlfjqVZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SjtWGhEVnMlfjqVZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SjtWGhEVnMlfjqVZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SjtWGhEVnMlfjqVZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 宕机
数据膨胀
客户端
MongoDB 单机
(容量有限 / 宕机即不可用)
业务中断
数据丢失风险
磁盘写满
无法继续写入
这两个问题对应的是两类完全不同的需求:可用性 (机器挂了服务还在)和容量扩展(数据多了存得下)。MongoDB 的集群方案就是围绕这两类需求设计的。
MongoDB 的三种集群方式
MongoDB 内置支持三种集群方式:主从集群、副本集群、分片集群。
| 集群方式 | 状态 | 解决的问题 | 说明 |
|---|---|---|---|
| 主从集群(Master-Slaver) | 官方已不推荐,被副本集群取代 | 单点故障(有限) | 旧方案,主从角色固定,故障需人工干预 |
| 副本集群(Replica Set) | 主流 | 单点故障 + 读性能扩展 | 多节点互为主从关系,自动故障转移 |
| 分片集群(Sharding) | 海量数据方案 | 单点故障 + 海量数据存储 | 数据水平拆分到多台机器,每台只存一部分 |
三点值得展开:
主从集群为什么被淘汰。 主从模式下谁是主、谁是从是写死的,主节点宕机后不能自动把从节点提升为主,恢复服务要人工介入。副本集群完全取代了它:多个机器之间互为主从关系,主节点挂掉后剩余节点自动选举出新的主节点继续服务,全程无需人工干预。「互为主从」这四个字就是新旧方案的分水岭。
副本集群解决什么。 副本集群的多个节点存放的是同一份数据,一台宕机,其余机器上还有完整数据,可以立刻接管服务,这解决的是单点故障。同时多个副本节点都能对外提供查询,读请求可以分摊,间接提升读性能。但它存不下超过单机容量的数据:三台机器各存一份完整数据,容量上限还是一台机器的容量。
分片集群解决什么。 分片集群把一份完整的数据拆开,分散存储到不同服务器上,每台服务器只保存数据的一部分,所有分片的数据之和才是完整数据集。这样 1TB 的数据可以由 4 台各存 250GB 的机器共同承载,容量随机器数量线性扩展,写请求也分摊到了多台机器上。分片集群在结构上同时具备高可用能力,但它的核心价值、也是最复杂的部分,在于海量数据的水平拆分。
两者的定位可以用一句话区分:副本集群是「多台机器存同一份数据」,分片集群是「多台机器各存一份数据的一部分」。前者保障可用性,后者保障容量。
交友项目什么时候需要集群
先给结论:搭建集群本身不是开发的重点,判断「什么时候需要集群」才是。在企业里,MongoDB 集群通常由专业的 DBA 或运维人员搭建,开发要掌握的是每种集群的特点和适用场景,能在项目里正确连接、正确验证。
回到交友项目本身。推荐用户、今日佳人、访客记录这些数据放在 MongoDB 里,项目初期量级并不大,推荐用户按用户维度存储,单机几百 GB 内绰绰有余。这个阶段要做的是保证可用性:MongoDB 宕机会直接影响首页推荐,这是核心入口,不能挂。所以合理的演进路径是:
#mermaid-svg-G4MdgcDkrEES6FNX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-G4MdgcDkrEES6FNX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-G4MdgcDkrEES6FNX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-G4MdgcDkrEES6FNX .error-icon{fill:#552222;}#mermaid-svg-G4MdgcDkrEES6FNX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-G4MdgcDkrEES6FNX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-G4MdgcDkrEES6FNX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-G4MdgcDkrEES6FNX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-G4MdgcDkrEES6FNX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-G4MdgcDkrEES6FNX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-G4MdgcDkrEES6FNX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-G4MdgcDkrEES6FNX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-G4MdgcDkrEES6FNX .marker.cross{stroke:#333333;}#mermaid-svg-G4MdgcDkrEES6FNX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-G4MdgcDkrEES6FNX p{margin:0;}#mermaid-svg-G4MdgcDkrEES6FNX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-G4MdgcDkrEES6FNX .cluster-label text{fill:#333;}#mermaid-svg-G4MdgcDkrEES6FNX .cluster-label span{color:#333;}#mermaid-svg-G4MdgcDkrEES6FNX .cluster-label span p{background-color:transparent;}#mermaid-svg-G4MdgcDkrEES6FNX .label text,#mermaid-svg-G4MdgcDkrEES6FNX span{fill:#333;color:#333;}#mermaid-svg-G4MdgcDkrEES6FNX .node rect,#mermaid-svg-G4MdgcDkrEES6FNX .node circle,#mermaid-svg-G4MdgcDkrEES6FNX .node ellipse,#mermaid-svg-G4MdgcDkrEES6FNX .node polygon,#mermaid-svg-G4MdgcDkrEES6FNX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-G4MdgcDkrEES6FNX .rough-node .label text,#mermaid-svg-G4MdgcDkrEES6FNX .node .label text,#mermaid-svg-G4MdgcDkrEES6FNX .image-shape .label,#mermaid-svg-G4MdgcDkrEES6FNX .icon-shape .label{text-anchor:middle;}#mermaid-svg-G4MdgcDkrEES6FNX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-G4MdgcDkrEES6FNX .rough-node .label,#mermaid-svg-G4MdgcDkrEES6FNX .node .label,#mermaid-svg-G4MdgcDkrEES6FNX .image-shape .label,#mermaid-svg-G4MdgcDkrEES6FNX .icon-shape .label{text-align:center;}#mermaid-svg-G4MdgcDkrEES6FNX .node.clickable{cursor:pointer;}#mermaid-svg-G4MdgcDkrEES6FNX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-G4MdgcDkrEES6FNX .arrowheadPath{fill:#333333;}#mermaid-svg-G4MdgcDkrEES6FNX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-G4MdgcDkrEES6FNX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-G4MdgcDkrEES6FNX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-G4MdgcDkrEES6FNX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-G4MdgcDkrEES6FNX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-G4MdgcDkrEES6FNX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-G4MdgcDkrEES6FNX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-G4MdgcDkrEES6FNX .cluster text{fill:#333;}#mermaid-svg-G4MdgcDkrEES6FNX .cluster span{color:#333;}#mermaid-svg-G4MdgcDkrEES6FNX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-G4MdgcDkrEES6FNX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-G4MdgcDkrEES6FNX rect.text{fill:none;stroke-width:0;}#mermaid-svg-G4MdgcDkrEES6FNX .icon-shape,#mermaid-svg-G4MdgcDkrEES6FNX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-G4MdgcDkrEES6FNX .icon-shape p,#mermaid-svg-G4MdgcDkrEES6FNX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-G4MdgcDkrEES6FNX .icon-shape .label rect,#mermaid-svg-G4MdgcDkrEES6FNX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-G4MdgcDkrEES6FNX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-G4MdgcDkrEES6FNX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-G4MdgcDkrEES6FNX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 数据量适中
但要求高可用
数据量突破单机容量上限
单机 MongoDB
(开发/验证阶段)
副本集群
(生产环境标配)
分片集群
(每片内部再配副本)
- 数据量适中、不允许服务中断 → 上副本集群。三节点副本集(一主两从)是中小型应用最常见的选择,成本可控,换来自动故障转移和读扩展。
- 数据量达到 TB 级、单机磁盘和写入都扛不住 → 上分片集群。注意分片集群里的配置服务、每个分片在高版本 MongoDB 中都要求以副本集形式部署,等于「分片负责容量、副本负责可用」的组合拳。
判断依据很简单:先看数据量,再看可用性要求。只为了单点故障,副本集群就够;数据量大到单机装不下,才上分片。一个日活几十万的应用,绝大多数走到副本集群这一步就足够了。
还有一点是搭建层面的提示:课程资料里提供了副本集群和分片集群基于 Docker 的搭建文档,感兴趣的可以按文档一步步搭起来验证;这里后续几篇会给出搭建要点和完整的验证命令,但不把篇幅耗在搭建过程本身。
常见误解澄清
| 误解 | 澄清 |
|---|---|
| 「集群 = 数据量大了才需要,小项目用不上」 | 副本集群解决的是单点故障,和数据量无关。只要 MongoDB 宕机会影响业务,就该上副本集,哪怕数据只有几个 GB |
| 「主从集群和副本集群是一回事,随便用哪个」 | 主从集群已被副本集群完全取代,主从角色固定、故障不能自动切换。新项目一律用副本集群 |
| 「上了分片集群就不需要副本集群了」 | 分片集群解决容量问题,可用性仍然要靠副本集保证。生产环境的分片集群中,配置服务和每个分片本身就是副本集形式 |
| 「加块硬盘就能解决存储容量问题,不需要分片」 | 单机加盘有上限,CPU、内存、写入吞吐同样会先到瓶颈。真正的海量数据只能靠水平拆分 |
| 「集群搭建是开发的基本功,必须精通」 | 集群搭建在企业中多由 DBA/运维负责,开发的核心职责是理解各模式特点、正确配置连接、能验证故障转移行为 |
总结
单机 MongoDB 的两个硬伤,单点故障和容量上限,分别由两种集群模式解决:副本集群用「多机同数据」换来高可用和读扩展,分片集群用「多机各存一部分」换来海量数据存储。主从集群作为旧方案已被淘汰。对交友项目来说,合理的路径是数据量适中时先上副本集群,等数据真正涨到单机装不下再演进到分片。
副本集群内部的节点角色划分、数据如何同步、主节点挂了怎么自动选主,下一篇MongoDB副本集群:执行概述与执行原理展开。