MongoDB(文档数据库)文档模型、复制、分片与查询路径
本篇面向高级开发面试与线上排障,沿“文档边界 -> 存储写入 -> 一致性 -> 查询索引 -> 副本集 -> 分片路由 -> 项目治理”建立证据链。项目版本统一写“待现场核对”;版本敏感行为必须使用版本核对卡在目标环境验证,不作“最新版”断言。文中容量、延迟和比例均为教学演绎,不代表生产现场。
1. 文档模型与存储边界
1.1 BSON(二进制 JSON 文档)、collection(集合)、document(文档)与大小边界
MongoDB(文档数据库)以 database(数据库)组织 collection(集合),collection(集合)保存 BSON(二进制 JSON 文档)形式的 document(文档)。BSON(二进制 JSON 文档)携带字段名与类型,方便表达嵌套对象和数组,但字段名重复、类型宽度与嵌套层级也会占空间。单个 document(文档)的 16 MiB(兆字节)限制是建模边界而不是容量目标:接近上限会放大网络传输、解码、缓存占用、更新搬移和复制成本;不可控附件、完整历史或持续增长数组应拆到独立 collection(集合)或对象存储。
| 层级 | 职责 | 主要约束 | 证据 |
|---|---|---|---|
| database(数据库) | 名称与权限管理边界 | 不自动等于业务聚合边界 | 连接目标、权限清单 |
| collection(集合) | 同类 document(文档)的治理单元 | 索引、校验、分片策略按集合实施 | 定义、索引、分片元数据 |
| document(文档) | 单文档原子读写与局部数据 | 16 MiB(兆字节)、数组与字段增长 | 文档尺寸分位数、更新分布 |
| field(字段)/array(数组) | 表达嵌套属性与一对多局部事实 | 类型漂移、无界增长、多键索引放大 | 模式抽样、索引统计 |
flowchart LR
D["database 数据库"] --> C["collection 集合"]
C --> O["document 文档"]
O --> F["field 字段"]
O --> A["array 数组"]
A --> L{"持续增长?"}
L -->|否| E["保持局部读取"]
L -->|是| S["拆分历史或附件"]
O --> M{"接近 16 `MiB`?"}
M -->|是| S
S -.失败回退.-> C图 1 说明。 节点表示从 database(数据库)到 field(字段)和 array(数组)的包含关系,实线箭头是正常建模路径,虚线是超限后回到 collection(集合)重新划界。前提是已量化单对象大小与增长率;正常路径让同一事务边界内的数据局部读取;失败路径是数组或附件持续增长并逼近 16 MiB(兆字节)。业务结论是文档上限必须在设计期变成容量门槛,不能等写入报错后再拆。
数据演绎 1:文档增长。 跨境物流一票货初始 document(文档)为 18 KiB(千字节),每条原始轨迹报文平均 2 KiB(千字节),每天 80 条、保存 120 天,理论新增约 2×80×120=19200 KiB,已经越过 16 MiB(兆字节)边界;即使压缩后暂时可写,单票查询也会拖入大量无关历史。应让主 document(文档)保留摘要和最新节点,轨迹事件按运单与时间拆分,并用幂等事件标识去重。
热门面试题
- 问题(基础题):BSON(二进制 JSON 文档)与普通 JSON(文本对象表示)在建模上有什么差异?
- 考点:类型、空间与读取边界。
- 回答思路:先讲类型表达,再讲字段名和嵌套带来的成本。
- 详细答案:BSON(二进制 JSON 文档)保存字段名、类型和值,支持日期、二进制等类型并便于随机解析;代价是字段名重复、类型元数据和嵌套都占空间。建模时要看文档尺寸分位数和投影列,不能把可表达等同于低成本。
- 进阶追问:二进制格式是否一定比文本更小?
- 进阶回答:不一定,字段名和类型元数据可能增加体积,实际大小应以样本文档测量。
- 问题(原理题):为什么不能把 16
MiB(兆字节)当成常规文档目标?- 考点:硬限制与性能预算。
- 回答思路:从网络、缓存、更新和复制四条路径回答。
- 详细答案:接近上限的 document(文档)会占用更多 cache(缓存)和网络,局部字段更新仍可能引发大对象重写、索引维护与副本传输。硬限制只说明能否保存,不说明延迟、写放大和故障恢复是否可接受。
- 进阶追问:大附件应放哪里?
- 进阶回答:通常放独立对象存储或专门的大对象方案,主 document(文档)只保存校验值、位置和业务元数据。
- 问题(场景题):轨迹数组为何容易成为反模式?
- 考点:无界数组、热点与多键索引。
- 回答思路:同时说明大小增长、更新冲突和索引项增长。
- 详细答案:轨迹会随时间持续追加,使同一 document(文档)变大并成为写热点;若数组字段建立索引,每个元素还可能展开为索引键。应按运单与时间拆事件,主文档只留有界摘要。
- 进阶追问:拆分后如何保证顺序?
- 进阶回答:使用业务事件时间、接收序号和幂等标识联合排序,并显式处理迟到与重复。
1.2 embedding(嵌入)、reference(引用)、聚合根与冗余
embedding(嵌入)把一起读取、一起修改且生命周期一致的数据放进聚合根,换取单 document(文档)原子性和读取局部性;reference(引用)把共享、独立演进或高基数事实拆开,以额外查询或 $lookup(关联阶段)成本换取唯一事实源。数据冗余不是天然错误,但必须有权威字段、刷新触发、失败补偿与重建方式。聚合根边界应由不变量和访问模式共同决定,而不是照搬对象关系或“能嵌就嵌”。
| 判断维度 | embedding(嵌入)倾向 | reference(引用)倾向 | 风险控制 |
|---|---|---|---|
| 读取 | 总是随根对象读取 | 独立读取或多方共享 | 记录查询形状与命中率 |
| 更新 | 同事务、低冲突 | 高频独立更新 | 幂等、版本与并发控制 |
| 基数 | 有界且可预测 | 高基数或无界 | 文档尺寸和增长告警 |
| 生命周期 | 同生同灭 | 保留期不同 | 删除传播与审计 |
flowchart TD
A["访问模式与不变量"] --> B{"同读同写且有界?"}
B -->|是| E["embedding 嵌入"]
B -->|否| R["reference 引用"]
E --> L["局部读取与单文档原子"]
R --> J["额外查询或关联成本"]
E --> X{"数组变为无界?"}
X -->|是| R
R --> Y{"共享事实发生漂移?"}
Y -->|是| C["按权威源重建冗余"]
C -.校验失败.-> A图 2 说明。 节点从访问模式与不变量开始,在 embedding(嵌入)和 reference(引用)之间决策,箭头表示正常选择与运行中重评。前提是知道基数、更新频率和生命周期;正常路径得到局部原子或共享事实;失败路径是数组失控或冗余漂移后回到边界设计。业务结论是空间、读取与一致性成本只能交换,不能消失。
数据演绎 2:商品扩展属性。 WMS(仓储管理系统)有 100 万商品,每个商品平均 24 个展示属性、每个属性 60 B(字节),嵌入约增加 1.44 KiB(千字节)正文,列表按商品读取时收益明确;若把供应商报价 3000 条也嵌入,每条 120 B(字节),单商品额外约 352 KiB(千字节)且报价高频变化。展示属性可嵌入商品聚合根,报价应引用独立事实并在商品侧只保留可重建摘要。
热门面试题
- 问题(基础题):embedding(嵌入)和 reference(引用)如何选择?
- 考点:访问模式、生命周期和基数。
- 回答思路:按同读同写、有界性、共享程度依次判断。
- 详细答案:同读同写、生命周期一致且基数有界的数据适合 embedding(嵌入);被多个聚合共享、独立高频更新或数量无界的数据适合 reference(引用)。最终还要用文档尺寸、冲突率和查询次数验证。
- 进阶追问:是否可以混合使用?
- 进阶回答:可以,常见做法是引用权威事实,同时嵌入少量可重建摘要,并记录版本或更新时间。
- 问题(原理题):为什么说嵌入是在用空间换局部性?
- 考点:反规范化收益与代价。
- 回答思路:比较一次读取和多份副本的更新传播。
- 详细答案:嵌入让相关字段随根 document(文档)一次读取并进入同一原子边界,但共享值会在多份文档重复保存。共享值变化时必须传播或接受短暂陈旧,因此省下的关联成本转化为存储和同步成本。
- 进阶追问:冗余如何防漂移?
- 进阶回答:明确单一权威源,用幂等事件更新摘要,并以版本、水位和定期校验发现漏更。
- 问题(场景题):库存数量能否嵌入商品文档?
- 考点:业务不变量与冲突热点。
- 回答思路:区分展示快照和裁决事实。
- 详细答案:可嵌入可重建的展示快照,但库存扣减涉及不超卖、仓库维度、流水与并发裁决,不能只靠灵活字段。权威事实仍需明确条件更新、幂等键、事务或等价约束,并通过流水对账。
- 进阶追问:展示值落后怎么办?
- 进阶回答:返回更新时间与一致性等级,关键裁决回权威路径,异步投影按事件水位修复。
1.3 schema validation(模式校验)、字段演进与反模式治理
灵活模式允许同一 collection(集合)的 document(文档)存在不同字段,但不等于无治理。schema validation(模式校验)应约束标识、金额、状态、时间、数组上限等关键字段;应用层再负责跨字段和跨文档不变量。字段演进要采用扩展读取、双读兼容、回填、校验、收敛写入和删除旧字段的阶段流程。常见反模式包括多态字段同名异型、动态字段爆炸、把字段名当业务数据、无界数组、超大文档和缺少版本标识。
| 变化类型 | 兼容策略 | 失败风险 | 核验证据 |
|---|---|---|---|
| 新增可选字段 | 先扩读后写入 | 老客户端忽略或误设默认值 | 客户端矩阵、缺失率 |
| 字段改名 | 双读、单写、回填 | 两字段值分叉 | 差异计数、回填水位 |
| 类型变化 | 新字段承载新类型 | 同名异型导致查询与排序异常 | 类型分布、拒绝计数 |
| 删除字段 | 停读后观察再删除 | 回滚版本仍依赖旧字段 | 发布依赖、回滚演练 |
stateDiagram-v2
[*] --> 扩展读取
扩展读取 --> 写入新字段
写入新字段 --> 历史回填
历史回填 --> 差异校验
差异校验 --> 收敛读取: 校验通过
差异校验 --> 历史回填: 发现漏写
收敛读取 --> 删除旧字段
删除旧字段 --> [*]
删除旧字段 --> 扩展读取: 回滚失败图 3 说明。 节点是字段演进状态,箭头是发布与数据收敛顺序。前提是新旧客户端可识别版本;正常路径在差异归零后删除旧字段;失败路径是漏写或回滚依赖旧字段时返回回填和扩读。业务结论是模式变化是应用、数据和回滚共同参与的迁移,不是一条批量更新命令。
数据演绎 3:字段类型漂移。 IoT(物联网)设备元数据有 500 万条,其中 firmwareVersion 原为字符串,2% 新接入设备错误写成数值,即 10 万条。按字符串范围排序会出现缺失或顺序异常,建立索引也不能修复语义。先拒绝新异型写入,再将新类型写到新字段,按设备型号分批回填并比较类型计数,差异归零后才收敛读取。
热门面试题
- 问题(基础题):灵活模式为什么不等于无模式?
- 考点:存储容忍与业务契约。
- 回答思路:区分数据库可接受和业务可解释。
- 详细答案:数据库能保存不同形状的 document(文档),但查询、索引、排序和业务状态机仍依赖字段类型与含义。关键字段必须用 schema validation(模式校验)、应用校验和数据质量指标共同治理。
- 进阶追问:所有字段都要强校验吗?
- 进阶回答:不必,扩展属性可保留弹性,但标识、金额、状态、时间和边界字段应优先约束。
- 问题(原理题):字段改名为什么要双读而不是直接批量更新?
- 考点:滚动发布与回滚窗口。
- 回答思路:从新旧客户端并存和回填时长解释。
- 详细答案:滚动发布期间老版本仍读旧字段,历史数据也不会瞬间完成回填。双读让新版本兼容两种形状,配合单写新字段、回填与差异校验,才能保留回滚能力。
- 进阶追问:何时能删旧字段?
- 进阶回答:新写已收敛、历史差异归零、老客户端退出且回滚演练不再依赖后才能删除。
- 问题(场景题):动态属性为什么可能造成字段爆炸?
- 考点:把业务值编码进字段名的代价。
- 回答思路:说明统计、索引、查询和治理成本。
- 详细答案:若每个传感器标识都成为字段名,文档形状会无限分裂,索引与查询难以复用,字段级质量统计也失去边界。应把标识和值建模为受控数组元素或独立事件。
- 进阶追问:受控数组就没有风险吗?
- 进阶回答:仍要限制元素数、单元素大小和索引范围,防止无界增长与多键索引放大。
2. 存储引擎与写入确认
2.1 WiredTiger(存储引擎)B-Tree(平衡树)、cache(缓存)、page(页)与压缩
WiredTiger(存储引擎)使用 B-Tree(平衡树)组织集合与索引数据,磁盘 page(页)经压缩后读入 cache(缓存),在内存中解压和修改。修改形成 dirty page(脏页),后台驱逐与 checkpoint(检查点)把稳定内容写回磁盘。cache(缓存)压力不是简单“内存不够”:大扫描会挤压热点 page(页),长事务可能牵制可回收版本,更新热点会制造大量 dirty page(脏页),压缩节省磁盘却消耗处理器。诊断必须同时观察缓存占用、脏页比例、驱逐、读入字节、写出字节和磁盘延迟。
| 组件 | 正常职责 | 压力信号 | 业务影响 |
|---|---|---|---|
| B-Tree(平衡树) | 按键定位集合记录和索引项 | 深度、随机读、页分裂增加 | 点查与范围查变慢 |
| cache(缓存) | 保存解压后的工作集 | 驱逐活跃、读入激增 | 尾延迟抬升 |
| dirty page(脏页) | 承载尚未稳定写出的修改 | 比例持续高、写出追不上 | 写入节流或抖动 |
| 压缩 | 降低磁盘与输入输出量 | 处理器繁忙、压缩比异常 | 容量与计算成本变化 |
flowchart LR
Q["查询或更新"] --> C["cache 缓存中的解压 page 页"]
C --> B["B-Tree 平衡树定位"]
B --> H{"命中工作集?"}
H -->|是| R["返回或修改"]
H -->|否| D["磁盘压缩 page 页"] --> C
R --> W["dirty page 脏页"]
W --> E["驱逐或 checkpoint 检查点"] --> D
C -.压力过高.-> T["驱逐抖动与节流"]图 4 说明。 节点表示磁盘压缩 page(页)、cache(缓存)解压 page(页)、B-Tree(平衡树)定位和 dirty page(脏页)回写,箭头表示读写循环。前提是工作集与缓存预算匹配;正常路径优先命中内存并后台稳定写出;失败路径是大扫描或写热点造成驱逐抖动。业务结论是加索引或加内存前先证明工作集、访问形状和脏页来源。
数据演绎 4:缓存工作集。 实例 cache(缓存)预算教学值为 24 GiB(吉字节),热点商品与索引解压后占 18 GiB(吉字节),一次无界聚合顺序读入 20 GiB(吉字节)冷 page(页)。即使磁盘压缩后仅 7 GiB(吉字节),进入 cache(缓存)仍按解压形态竞争,可能驱逐热点并使点查从 5 ms(毫秒)升到 80 ms(毫秒)。止血应限制扫描、恢复索引剪枝并控制并发,而不是只看磁盘剩余量。
热门面试题
- 问题(基础题):WiredTiger(存储引擎)的 page(页)为何有磁盘与内存两种形态?
- 考点:压缩存储与缓存访问。
- 回答思路:从磁盘容量和内存计算路径回答。
- 详细答案:磁盘 page(页)通常压缩以减少空间和输入输出,读入 cache(缓存)后解压便于查找与修改。容量估算不能直接把磁盘文件大小当成内存工作集。
- 进阶追问:压缩比越高越好吗?
- 进阶回答:不一定,还要衡量压缩与解压的处理器成本、延迟和数据分布。
- 问题(原理题):dirty page(脏页)持续升高说明什么?
- 考点:前台修改与后台稳定写出的速率差。
- 回答思路:检查写入、检查点、驱逐和磁盘四类证据。
- 详细答案:它表示缓存中待写出的修改积累,可能来自写入突增、磁盘变慢、热点更新或检查点压力。需要对齐产生速率、写出速率和尾延迟,不能只凭一个比例判故障。
- 进阶追问:能否直接缩小缓存迫使写出?
- 进阶回答:高峰期贸然调整可能加剧驱逐和读放大,应先限流并在目标版本验证参数行为。
- 问题(场景题):点查突然变慢但处理器不高,先看什么?
- 考点:工作集被驱逐与随机读。
- 回答思路:把缓存读入、驱逐和磁盘延迟按时间关联。
- 详细答案:先看是否有大扫描导致 cache(缓存)读入和驱逐激增,再看点查索引是否仍被采用、磁盘随机读尾延迟是否上升。处理器不高不能排除输入输出瓶颈。
- 进阶追问:重启能否解决?
- 进阶回答:重启会清空热 cache(缓存)并掩盖现场,除非已确认恢复收益与风险,否则先保留证据并止住异常负载。
2.2 journal(预写日志)、checkpoint(检查点)、oplog(操作日志)与崩溃恢复
客户端写入 Primary(主节点)后,WiredTiger(存储引擎)在 cache(缓存)修改 page(页),journal(预写日志)提供检查点之间的崩溃恢复记录,checkpoint(检查点)周期性形成可恢复的数据文件一致点;复制层把操作写入 oplog(操作日志),Secondary(从节点)拉取并应用。客户端何时收到确认取决于 write concern(写关注)及其持久化配置,而不是等所有数据 page(页)写回。崩溃后节点先从最后 checkpoint(检查点)恢复,再用 journal(预写日志)重放;副本集层还要依据 oplog(操作日志)追赶到当前任期的有效历史。
| 时点 | 已获得 | 尚未获得 | 失败处理 |
|---|---|---|---|
| 主节点内存修改 | 本次操作已执行 | 持久化、复制与业务完成 | 不能向外宣称成功 |
| journal(预写日志)满足配置 | 本节点可按配置恢复 | 多数副本与外部系统完成 | 结合写关注判断 |
| majority(多数)确认 | 多数投票成员达到要求 | 消费者、资金渠道、对账完成 | 记录幂等键与水位 |
| checkpoint(检查点)推进 | 缩短本节点恢复重放范围 | 不替代副本和备份 | 做崩溃与恢复演练 |
sequenceDiagram
participant C as 客户端
participant P as Primary(主节点)
participant J as journal(预写日志)
participant O as oplog(操作日志)
participant S as Secondary(从节点)
C->>P: 带幂等键写入
P->>P: 修改 cache(缓存)page(页)
P->>J: 追加并按配置刷盘
P->>O: 生成复制记录
O->>S: 拉取并应用
S-->>P: 报告复制进度
P-->>C: write concern(写关注)满足
Note over C,S: 外部事件与业务对账仍是独立完成边界图 5 说明。 节点是客户端、主节点、两类日志与从节点,箭头按时间展示正常确认路径。前提是任期有效且配置已核对;正常路径先修改、记录 journal(预写日志)和 oplog(操作日志),再按写关注确认;失败路径是响应丢失或副本落后造成结果未知。业务结论是返回成功只对应数据库配置承诺,不等于跨系统业务完成。
sequenceDiagram
participant P as 崩溃节点
participant K as checkpoint(检查点)
participant J as journal(预写日志)
participant O as oplog(操作日志)
participant R as 恢复后节点
P-xP: 进程或主机故障
K->>R: 装载最近一致点
J->>R: 重放检查点后记录
O->>R: 与副本集有效历史对齐
alt 历史可追赶
R->>R: 恢复服务并继续追赶
else 历史分叉或窗口不足
R->>R: 回滚或重新同步
end图 6 说明。 节点分开本地 checkpoint(检查点)、journal(预写日志)恢复与副本集 oplog(操作日志)对齐,箭头表示先恢复存储再确认复制历史。前提是文件与日志连续可读;正常路径重放后追赶;失败路径是历史分叉或 oplog(操作日志)窗口不足,需要回滚或重新同步。业务结论是“进程启动”不等于副本已经安全回归服务。
数据演绎 5:写确认与恢复。 三投票成员副本集中,Primary(主节点)与一个 Secondary(从节点)在 12 ms(毫秒)内达到多数要求,另一个从节点延迟 8 s(秒),客户端可在约 12 ms(毫秒)后收到多数确认;随后主节点故障,新主若来自已确认集合可保留该写。若响应在第 13 ms(毫秒)丢失,客户端仍面对未知结果,应按业务幂等键查询新主,而不是直接重放扣减。
热门面试题
- 问题(基础题):journal(预写日志)与 oplog(操作日志)有什么区别?
- 考点:本地恢复与副本复制职责。
- 回答思路:按作用范围、消费方和故障场景区分。
- 详细答案:journal(预写日志)主要服务 WiredTiger(存储引擎)在 checkpoint(检查点)之间的本地崩溃恢复;oplog(操作日志)是副本集成员同步和追赶的逻辑历史。两者都不能单独替代独立备份。
- 进阶追问:有副本为何仍需 journal(预写日志)?
- 进阶回答:节点自身仍可能崩溃并恢复,副本也可能同时落后或不可用;职责不同,不能互相抵消。
- 问题(原理题):checkpoint(检查点)为什么不是逐事务确认点?
- 考点:日志承诺与数据页稳定化解耦。
- 回答思路:说明前台延迟与后台刷页的分工。
- 详细答案:逐事务等待所有相关 page(页)写回会放大随机输入输出。系统先用日志记录恢复所需变化并按 write concern(写关注)确认,checkpoint(检查点)再批量推进数据文件一致点。
- 进阶追问:检查点越频繁越安全吗?
- 进阶回答:不能绝对化,频率会影响恢复范围、写入峰值和存储压力,必须在目标版本与工作负载下验证。
- 问题(场景题):客户端超时后能否直接重试写入?
- 考点:未知结果与幂等。
- 回答思路:区分明确失败和响应丢失。
- 详细答案:不能把超时当失败,写入可能已达到多数确认但响应丢失。应携带稳定幂等键,优先查询权威 Primary(主节点)上的结果,再由可重试写或业务幂等返回原结果。
- 进阶追问:数据库可重试写能覆盖外部支付吗?
- 进阶回答:不能,外部渠道副作用仍需独立幂等、查单、补偿与对账。
2.3 write concern(写关注)、read concern(读关注)与多数提交点
write concern(写关注)描述一次写在返回前要达到的成员数量与持久化条件;read concern(读关注)描述读取允许观察到哪一层确认历史;read preference(读偏好)描述把读请求发往哪类成员。三者不能互相替代。majority commit point(多数提交点)是副本集认为已被多数投票成员接受、不会因正常选举轻易丢失的历史边界,但不同成员应用到该点仍有时间差。多数确认提升耐久性,却增加等待副本的延迟,并在多数不可用时牺牲写可用性;它不覆盖消息消费、渠道扣款和对账。
| 控制项 | 回答的问题 | 不能保证 | 核验证据 |
|---|---|---|---|
| write concern(写关注) | 写到什么程度才返回 | 读路由、外部业务完成 | 客户端配置、确认延迟 |
| read concern(读关注) | 允许读到哪类历史 | 请求一定落在最新成员 | 会话、读取时间点 |
| read preference(读偏好) | 优先从哪个成员读 | 无陈旧读、读己之写 | 选点日志、复制延迟 |
| majority commit point(多数提交点) | 哪段历史已获多数承诺 | 所有成员已应用 | 成员进度、任期与提交点 |
sequenceDiagram
participant C as 客户端会话
participant P as Primary(主节点)
participant S1 as Secondary A(从节点 A)
participant S2 as Secondary B(从节点 B)
C->>P: write concern(写关注)为多数的写入
P->>S1: 复制并等待
P->>S2: 复制延迟
S1-->>P: 到达多数条件
P-->>C: 返回成功并携带会话时间
C->>S2: 按从节点偏好读取
alt S2 已达到会话时间
S2-->>C: 可见刚才写入
else S2 尚未追平
S2-->>C: 等待、换节点或返回陈旧结果
end图 7 说明。 节点是客户端会话、主节点和两个从节点,箭头区分写确认与后续读路由。前提是驱动会传递会话时间且关注级别已现场核对;正常路径在目标成员达到时间后读己之写;失败路径是读偏好选中落后成员。业务结论是多数写与从节点读组合仍需会话和水位约束。
数据演绎 6:延迟与可用性。 三成员中主节点本地 4 ms(毫秒)、从节点 A 往返 14 ms(毫秒)、从节点 B 往返 220 ms(毫秒)。多数写通常受较快从节点约束,可能约 18 ms(毫秒)确认;若 A 故障,只剩 B 可组成多数,确认延迟可能跃升到 224 ms(毫秒);若两从节点均不可达,多数写无法按原承诺成功。降低关注级别虽可能恢复吞吐,却改变耐久边界,必须由业务批准。
热门面试题
- 问题(基础题):三种 concern(关注)与 preference(偏好)分别管什么?
- 考点:确认、可见和路由的分层。
- 回答思路:用写到哪、读哪层、去哪里三个问题回答。
- 详细答案:write concern(写关注)约束写返回条件,read concern(读关注)约束可观察历史,read preference(读偏好)选择读取成员。任何一项都不能单独保证端到端一致性。
- 进阶追问:从节点偏好是否等于分担读且无代价?
- 进阶回答:不等于,复制延迟会带来陈旧读,故障切换还会改变可选成员。
- 问题(原理题):majority commit point(多数提交点)为何不等于所有副本可见?
- 考点:多数接受与成员应用进度。
- 回答思路:区分复制确认水位和各成员重放水位。
- 详细答案:多数提交点只说明足够成员承诺了这段历史,落后成员可能尚未拉取或应用。读到某成员时仍要看它的应用进度和读取语义。
- 进阶追问:多数提交后旧主的写一定保留吗?
- 进阶回答:在正常副本集选举语义下耐久性显著增强,但仍须按目标版本、拓扑和确认配置核对,不能外推到跨系统。
- 问题(场景题):库存写成功后从节点查不到怎么办?
- 考点:读己之写和权威读路径。
- 回答思路:先证明写水位,再检查读路由与成员进度。
- 详细答案:先记录写响应和会话时间,确认后续读是否保持同一会话、采用何种 read concern(读关注)并选中哪个成员。关键裁决可回 Primary(主节点),非关键展示可等待或返回陈旧标识。
- 进阶追问:能否靠睡眠固定时间解决?
- 进阶回答:不能,复制延迟随故障和负载变化,应使用会话水位、权威读或明确陈旧预算。
会话因果一致性、可重试读写、未知结果与多文档事务补充
会话因果一致性用逻辑时间维持“先写后读”“先读后写”等因果顺序,但依赖同一逻辑会话、兼容的读写设置和驱动传播,不能变成跨服务自动因果链。retryable read/write(可重试读写)让驱动在特定网络和主节点切换错误下重发可识别操作,降低瞬时故障暴露;业务仍要提供幂等键处理驱动范围外的重放和外部副作用。多文档 transaction(事务)能扩展原子边界,却增加锁、缓存、日志、提交协调与失败恢复成本;提交响应丢失时结果仍可能未知。
sequenceDiagram
participant A as 应用
participant P as Primary(主节点)
participant N as 新 Primary(主节点)
A->>P: 开始 transaction(事务)并更新两文档
P->>P: 暂存事务修改
A->>P: 提交
P-xA: 提交响应丢失
P-xP: 主节点故障
A->>N: 使用会话与事务标识确认结果
alt 已提交
N-->>A: 返回原成功结果
else 明确未提交
A->>N: 按幂等键重新执行
else 仍未知
N-->>A: 进入查单与对账
end图 8 说明。 节点展示应用、旧主和新主,箭头覆盖事务提交响应丢失与故障切换。前提是应用保存会话、事务和业务幂等标识;正常路径确认原结果或安全重试;失败路径是历史尚未收敛而进入对账。业务结论是事务原子性不消除网络边界上的未知结果。
数据演绎 7:事务持有成本。 Runner(执行器)批量修改 5000 个配置 document(文档),若每批 500 个、事务持续 8 s(秒),故障重试会重复占用较多缓存和提交协调资源;改为每批 50 个、稳定幂等键、单批 300 ms(毫秒),虽然有 100 个批次,但失败定位和重试范围更小。若业务要求 5000 个配置瞬时生效,应改为写新版本后原子切换版本指针,而不是维持超大事务。
3. 查询、索引与执行路径
3.1 查询形状、复合索引与 ESR(相等排序范围)原则
查询形状由过滤字段、操作符、排序、投影、分页和聚合共同定义。复合索引设计常用 ESR(相等排序范围)原则:高复用的 equality(相等)条件通常置前,随后安排能承接 sort(排序)的键,再放 range(范围)键;但这是成本启发式,不是脱离数据分布的口诀。索引前缀、字段基数、数组展开、排序方向和分片路由都可能改变最优次序。应以代表性参数和 explain(执行计划)的候选路径、扫描键数、读取文档数、排序阶段和返回行数验证。
| 查询形状 | 候选索引 | 期待路径 | 退化信号 |
|---|---|---|---|
| 租户相等、状态相等、时间倒序 | {tenantId:1,status:1,createdAt:-1} | 定位连续索引区间并有序返回 | 扫描键远大于返回数 |
| 租户相等、时间范围 | {tenantId:1,createdAt:1} | 前缀定位后范围扫描 | 缺租户导致全域扫描 |
| 状态低选择率 | 需结合其他高区分字段 | 避免大量回表 | 读取文档接近集合规模 |
| 非前缀字段排序 | 重设索引或限制结果集 | 索引提供排序 | 出现阻塞排序和溢写 |
flowchart LR
Q["查询形状"] --> E["equality 相等前缀"]
E --> S["sort 排序键"]
S --> R["range 范围键"]
R --> I["复合索引区间"]
I --> F["必要回表与过滤"]
F --> O["返回结果"]
Q --> X{"缺少前缀或选择率低?"}
X -->|是| C["大量扫描或阻塞排序"]
C -.用 explain 执行计划复核.-> Q图 9 说明。 节点把查询形状映射到 ESR(相等排序范围)索引区间,箭头是正常剪枝与失败回查。前提是参数分布有代表性;正常路径让索引同时承担过滤和排序;失败路径是缺前缀或低选择率导致大量扫描。业务结论是索引名称不重要,扫描量与返回量的比例才是证据。
数据演绎 8:低选择率。 1000 万条 Runner(执行器)任务中 status=SUCCESS 占 92%,单列状态索引查询成功任务要扫描约 920 万个键并大量回表,可能不如顺序读取。若查询变为 tenantId=42 AND status=FAILED,该租户 10 万条且失败率 0.5%,复合索引只需定位约 500 条。相同字段在不同查询形状下价值完全不同。
热门面试题
- 问题(基础题):什么是查询形状?
- 考点:索引设计输入。
- 回答思路:列出过滤、排序、投影、分页与聚合。
- 详细答案:查询形状不是一段固定文本,而是操作符结构、字段组合、排序、投影、限制和聚合阶段的共同模式。相同字段使用不同操作符,可能需要不同访问路径。
- 进阶追问:只看慢查询文本够吗?
- 进阶回答:不够,还要看参数分布、返回量、扫描量、路由目标和并发。
- 问题(原理题):ESR(相等排序范围)原则为什么不是绝对规则?
- 考点:启发式与成本验证。
- 回答思路:说明选择率、排序和范围会互相制约。
- 详细答案:相等前缀便于缩小区间,排序键可避免阻塞排序,范围键通常终止后续连续前缀利用;但极高选择率范围或无需排序的场景可能改变次序。最终必须用数据与计划验证。
- 进阶追问:多个相等字段如何排序?
- 进阶回答:结合查询复用、选择率、分片键前缀和索引数量评估,不靠固定口诀。
- 问题(场景题):为什么索引已命中查询仍慢?
- 考点:命中不等于高选择率。
- 回答思路:比较扫描键、读取文档、返回行和排序。
- 详细答案:索引可能扫描数百万低选择率键、回表读取大文档,之后再过滤或排序。应从
explain(执行计划)查看各阶段数量与耗时,而不是只看使用了哪个索引。 - 进阶追问:第一步就删索引吗?
- 进阶回答:不能,先确认其他查询依赖和写入成本,再通过隐藏、灰度或目标版本支持的方式验证影响。
3.2 多键、唯一、部分、稀疏、TTL(生存时间)、哈希与通配索引
multikey index(多键索引)为数组元素展开索引键,适合有界数组查询,但会增加索引项、写放大并限制某些复合组合。unique index(唯一索引)表达键唯一,不自动表达所有业务条件;partial index(部分索引)只索引满足条件的文档,适合稀疏活跃集合;sparse index(稀疏索引)按字段存在性减少条目,但缺失与空值语义要核对;TTL index(生存时间索引)用于后台过期清理,不是精确到秒的业务定时器。hashed index(哈希索引)利于均匀分布但弱化范围局部性,wildcard index(通配索引)覆盖动态字段却可能扩大索引和计划空间。
| 索引类型 | 主要收益 | 主要代价 | 适用边界 |
|---|---|---|---|
multikey index(多键索引) | 数组元素检索 | 每元素索引项、覆盖受限 | 有界标签和属性 |
partial index(部分索引) | 缩小活跃数据索引 | 查询谓词需匹配条件 | 未完成任务、有效配置 |
TTL index(生存时间索引) | 自动清理过期数据 | 删除非精确定时、产生写负载 | 缓存性和合规性过期 |
hashed/wildcard index(哈希/通配索引) | 均匀路由或动态字段探索 | 范围、空间和治理成本 | 明确受控场景 |
flowchart TD
W["字段与访问模式"] --> A{"数组?"}
A -->|是| M["数组多键索引"]
A -->|否| P{"只查子集?"}
P -->|是| PI["条件部分索引"]
P -->|否| U["普通或唯一索引"]
W --> T{"按时间过期?"}
T -->|是| TI["生存时间索引"]
W --> H{"均匀路由或动态字段?"}
H --> HI["哈希或通配索引"]
M -.数组失控.-> R["拆分集合并重设边界"]图 10 说明。 节点按字段形态和访问目标选择索引,箭头表示适用路径与数组失控后的重构。前提是已统计元素数、缺失率和过期精度;正常路径用较小索引服务明确查询;失败路径是动态字段或数组无限增长。业务结论是“一个万能索引”会把读便利转化为写放大和治理债务。
数据演绎 9:数组索引放大。 200 万设备 document(文档)平均有 40 个标签,multikey index(多键索引)理论产生约 8000 万个索引键;若每设备改一个标签却整体更新数组,还会触发旧键删除与新键插入。若 95% 查询只按 6 个受控标签筛选,可把受控标签提取为固定字段或独立映射,索引体积和更新成本会显著下降。
热门面试题
- 问题(基础题):多键索引如何处理数组?
- 考点:数组元素到索引键的展开。
- 回答思路:解释查询收益与键数量增长。
- 详细答案:multi
keyindex(多键索引)把数组中的可索引元素展开为多个键,使元素匹配可走索引;代价是元素越多,索引项、维护与扫描越多,并存在复合和覆盖限制。 - 进阶追问:数组只有一个元素也会怎样?
- 进阶回答:索引属性与字段实际形态有关,具体元数据和组合限制要在目标版本核对。
- 问题(原理题):partial
index(部分索引)和 sparseindex(稀疏索引)如何区分?- 考点:谓词过滤与字段存在性。
- 回答思路:比较索引纳入条件和查询匹配要求。
- 详细答案:partial
index(部分索引)用显式过滤条件选择文档,表达能力通常更强;sparseindex(稀疏索引)主要围绕字段是否存在。两者对缺失、空值和唯一性的语义不能混用。 - 进阶追问:查询为何可能不用部分索引?
- 进阶回答:若优化器不能证明查询结果完全落在部分条件内,使用它可能漏结果,因此会放弃。
- 问题(场景题):
TTL(生存时间)能否做订单超时关单?- 考点:后台清理与业务定时器边界。
- 回答思路:区分数据过期删除和业务状态迁移。
- 详细答案:
TTLindex(生存时间索引)适合最终清理,执行时间并非严格业务时刻,也不会自动完成关单副作用。订单关单应由可观测调度和幂等状态机执行,过期索引只承担后续保留治理。 - 进阶追问:删除峰值如何控制?
- 进阶回答:分散过期时间、控制写入批次并监控删除、复制和磁盘水位,具体能力现场核对。
3.3 覆盖查询、排序、分页、聚合管道与 explain(执行计划)
covered query(覆盖查询)要求过滤和返回字段都能由索引满足,且受多键、字段语义与执行计划限制;它减少 document(文档)读取,却不消除索引扫描。排序只有在索引键顺序、方向和前缀条件匹配时才能流式完成,否则会出现 blocking sort(阻塞排序)。大偏移分页仍要扫描并丢弃前面的结果,稳定的游标分页应使用唯一有序键作为续查条件。aggregation pipeline(聚合管道)要尽早过滤和投影,控制分组基数;内存不足时是否允许 disk spill(磁盘溢写)及其阈值属于版本敏感行为,必须核对。
| 计划指标 | 含义 | 健康示例 | 风险示例 |
|---|---|---|---|
| 扫描键数/返回数 | 索引剪枝效率 | 接近 1 | 数千倍 |
| 读取文档数/返回数 | 回表与过滤成本 | 0 或接近 1 | 接近集合规模 |
| 排序阶段 | 是否由索引提供顺序 | 无阻塞排序 | 大内存排序或溢写 |
| 分片目标数 | 路由剪枝 | 单分片 | 全分片广播 |
flowchart LR
Q["查询或 aggregation pipeline 聚合管道"] --> M["尽早 match 过滤"]
M --> P["project 投影缩窄"]
P --> I{"索引覆盖并提供排序?"}
I -->|是| C["顺序读取索引"]
I -->|否| F["读取 document 文档"]
F --> S["blocking sort 阻塞排序"]
S --> G["group 分组"]
G --> O{"内存足够?"}
O -->|否| D["disk spill 磁盘溢写或失败"]
C --> R["游标分页返回"]
D -.计划修正.-> M图 11 说明。 节点按过滤、投影、覆盖、排序、分组和溢写展示查询路径,箭头区分索引顺序路径与阻塞路径。前提是 explain(执行计划)使用代表性数据;正常路径尽早缩小数据并游标分页;失败路径是阻塞排序或溢写。业务结论是聚合优化优先减少进入昂贵阶段的数据量。
数据演绎 10:分页与溢写。 订单集合 3000 万条,按 createdAt 倒序每页 50 条。第 20 万页用偏移方式需越过约 1000 万条,即使走索引也要扫描并丢弃;改为用上一页 (createdAt,_id) 作为续查键,每页稳定扫描约 50 至数十条。另一个按设备分组的聚合若输入 2 亿条、分组 500 万个,每组状态 80 B(字节),仅状态约 381 MiB(兆字节),还未计哈希结构开销,容易触发溢写。
热门面试题
- 问题(基础题):什么是 covered query(覆盖查询)?
- 考点:索引完成过滤与投影。
- 回答思路:先定义,再说明扫描量仍可能很大。
- 详细答案:过滤条件和返回字段都可从索引项获得时,计划可能无需读取完整 document(文档)。但低选择率仍会扫描大量索引键,多键和缺失字段语义也可能破坏覆盖。
- 进阶追问:覆盖查询一定最快吗?
- 进阶回答:不一定,宽索引会增加缓存与写成本,仍应比较实际扫描、延迟和维护代价。
- 问题(原理题):为什么深分页会越来越慢?
- 考点:偏移丢弃成本。
- 回答思路:用扫描前缀数量解释。
- 详细答案:偏移分页为了返回后面的少量记录,仍需定位并越过前面大量结果;页码越深,扫描和丢弃越多。游标分页利用稳定唯一排序键从上次位置续查。
- 进阶追问:只用时间字段做游标够吗?
- 进阶回答:可能有同值导致重复或遗漏,通常增加唯一标识作为确定性次序。
- 问题(场景题):慢聚合如何用 explain(执行计划)排查?
- 考点:阶段级数量与资源证据。
- 回答思路:从路由、扫描、过滤、排序、分组和溢写逐层定位。
- 详细答案:先确认命中分片数,再比较扫描键、读取文档和返回数,检查过滤是否前推、排序是否阻塞、分组基数及是否磁盘溢写。随后用代表性参数复现并做索引或管道改写。
- 进阶追问:允许溢写就解决了吗?
- 进阶回答:没有,溢写把内存压力转成磁盘和延迟压力,只是保护机制,仍需缩小输入与基数。
4. 副本集与分片路由
4.1 副本集选举任期、oplog(操作日志)追赶、回滚与故障切换
副本集用选举产生 Primary(主节点),term(任期)区分领导历史;具有足够新 oplog(操作日志)的合格成员才有机会当选。Secondary(从节点)持续拉取并应用 oplog(操作日志),复制延迟要拆为网络获取、磁盘持久化与应用落后。旧主失去多数后应停止接受有效主写;若其上存在未进入多数历史的写,重新加入时可能 rollback(回滚)到获胜历史。故障切换期间驱动重新发现拓扑,正在执行的事务和未知结果要按会话、幂等键与权威新主恢复。
| 阶段 | 正常证据 | 失败风险 | 应用动作 |
|---|---|---|---|
| 心跳与选举 | 任期递增、成员多数可达 | 频繁选举、时钟或网络抖动 | 暂缓非关键写、保留时间线 |
| oplog(操作日志)追赶 | 获取与应用水位推进 | 窗口不足、磁盘慢 | 限流、扩窗口或重新同步 |
| 新主服务 | 驱动发现新 Primary(主节点) | 陈旧连接、结果未知 | 幂等重试与权威查单 |
| 旧主回归 | 与胜出历史一致 | 未多数写回滚 | 对账回滚记录与业务流水 |
sequenceDiagram
participant C as 客户端
participant P as 旧 Primary(主节点,任期 41)
participant S1 as Secondary A(从节点 A)
participant S2 as Secondary B(从节点 B)
C->>P: 写入操作 X
P->>S1: 复制 X
P-xS2: 网络分区
P-xS1: 失去多数
S1->>S2: 选举并进入任期 42
S1->>S1: 成为新 Primary(主节点)
C->>S1: 重新发现并按幂等键查结果
P->>S1: 恢复连接并对齐历史
alt X 已多数提交
S1-->>C: 返回 X 的原结果
else X 未多数提交
P->>P: rollback(回滚)分叉写
S1-->>C: 明确重试或对账
end图 12 说明。 节点展示旧主、两个从节点与客户端,箭头覆盖写入、失去多数、选举和历史对齐。前提是副本集多数规则有效;正常路径由更新历史成员当选并让客户端重连;失败路径是旧主未多数写被回滚。业务结论是切换成功还要检查回滚与未知业务结果。
数据演绎 11:oplog(操作日志)窗口。 oplog(操作日志)可保留 900 GiB(吉字节)历史,平时写入 15 MiB/s(兆字节每秒),理论窗口约 900×1024/15/3600≈17 小时;促销峰值升到 60 MiB/s(兆字节每秒)时只剩约 4.3 小时。某从节点离线 6 小时,平时可追赶,峰值却可能超窗而需要重新同步,因此窗口必须按峰值写率和最长维修时长计算。
热门面试题
- 问题(基础题):term(任期)解决什么问题?
- 考点:领导历史与陈旧主识别。
- 回答思路:说明新任期如何压过旧领导历史。
- 详细答案:term(任期)为每轮领导历史提供单调边界,成员可据此拒绝陈旧主的继续领导。它配合多数选举降低双主风险,但不能替代应用幂等和业务对账。
- 进阶追问:任期变多一定是故障吗?
- 进阶回答:计划切换也会增加任期,关键是频率、原因和业务影响是否异常。
- 问题(原理题):为什么会发生 rollback(回滚)?
- 考点:分叉历史与多数提交。
- 回答思路:从旧主孤立写和新主获胜历史解释。
- 详细答案:旧主在失去多数前后可能留下未被胜出多数历史接受的写,回归后必须丢弃分叉并对齐新主。多数确认可缩小此窗口,但未按多数确认的写仍需业务检查。
- 进阶追问:回滚文件存在就算处理完吗?
- 进阶回答:不算,还要把回滚记录映射到订单、库存或配置业务键,决定重放、补偿或人工对账。
- 问题(场景题):频繁选举如何排查?
- 考点:网络、资源与成员状态证据链。
- 回答思路:对齐心跳、任期、磁盘、暂停和连接时间线。
- 详细答案:先统计任期变化和业务错误,再看成员心跳丢失、网络抖动、磁盘尾延迟、进程暂停和维护操作。止血可降低非关键负载并修复不稳定成员,不能盲目提高超时掩盖根因。
- 进阶追问:修复后如何回归?
- 进阶回答:注入单成员故障和网络延迟,验证选举次数、恢复时间、未知结果率及幂等命中。
4.2 shard key(分片键)、mongos(路由进程)、config server(配置服务器)与路由
分片集合由 shard key(分片键)决定 document(文档)归属与查询剪枝。mongos(路由进程)读取 config server(配置服务器)维护的 chunk(数据块)范围和版本,把带完整分片键的请求定向到目标 shard(分片);缺少可用分片键时可能 scatter-gather(广播汇总)到所有分片。hashed sharding(哈希分片)可打散单调键写热点,但不保留原值范围局部性;ranged sharding(范围分片)利于范围查询和 zone(区域)约束,却要防止单调增长、低基数和大租户热点。分片键实际上编码主要访问模式、隔离方式和未来迁移成本。
| 设计 | 路由收益 | 主要风险 | 适用场景 |
|---|---|---|---|
| hashed sharding(哈希分片) | 高基数键较均匀分布 | 范围查询扇出 | 标识点查、写入打散 |
| ranged sharding(范围分片) | 范围局部与 zone(区域)放置 | 单调热点、块不均 | 时间窗口或地域查询 |
| 复合分片键 | 兼顾租户与时间/标识 | 前缀缺失仍广播 | 多租户定向访问 |
| 低基数键 | 设计简单 | 无法均匀拆分 | 通常不应单独使用 |
sequenceDiagram
participant C as 客户端
participant M as mongos(路由进程)
participant K as config server(配置服务器)
participant A as Shard A(分片 A)
participant B as Shard B(分片 B)
participant D as Shard C(分片 C)
M->>K: 获取 chunk(数据块)范围与版本
C->>M: tenantId=7 且 orderId=9001
M->>B: 定向请求
B-->>M: 局部结果
M-->>C: 返回
C->>M: 仅按 status 查询
par 广播
M->>A: 查询
M->>B: 查询
M->>D: 查询
end
A-->>M: 局部结果
B-->>M: 局部结果
D-->>M: 局部结果
M-->>C: 汇总与排序图 13 说明。 节点是客户端、路由进程、配置服务器和三个分片,箭头对比完整分片键的定向查询与缺键广播。前提是路由元数据新鲜且谓词可提取分片键;正常路径只访问一个分片;失败路径是全分片查询后在路由层汇总。业务结论是分片内命中索引也无法抵消跨分片扇出。
数据演绎 12:定向与广播。 8 个分片各有 5000 万条轨迹。按 tenantId + shipmentId 查询单票,完整分片键只访问 1 个分片并扫描约 30 条;只按 status=IN_TRANSIT 查询会扇出 8 个分片,若各返回 10 万候选,路由层需处理 80 万条再排序。并发 100 个同类广播请求会把局部慢查询放大成集群网络与内存压力。
热门面试题
- 问题(基础题):mongos(路由进程)如何定位分片?
- 考点:分片键谓词与元数据。
- 回答思路:从配置元数据到数据块归属说明。
- 详细答案:mongos(路由进程)根据 config server(配置服务器)中的 chunk(数据块)范围与版本,提取查询中的 shard
key(分片键)条件并计算目标。无法剪枝时会向多个甚至全部分片扇出。 - 进阶追问:路由进程保存权威数据吗?
- 进阶回答:它主要协调与路由,权威业务数据在分片上,集群元数据由配置服务器管理。
- 问题(原理题):哈希分片为何削弱范围局部性?
- 考点:原值顺序与哈希顺序。
- 回答思路:说明相邻原值会被打散。
- 详细答案:hashed sharding(哈希分片)按哈希值划分,相邻时间或编号通常落到不同区间,因此范围查询可能访问多个分片。它换来写入分散,但牺牲原值顺序局部。
- 进阶追问:能否用复合键折中?
- 进阶回答:可以围绕租户、哈希和时间组合评估,但必须用真实查询证明定向率、热点和块分布。
- 问题(场景题):如何识别热点分片?
- 考点:路由、资源与键分布证据。
- 回答思路:同时看请求目标、写率、块分布和硬件水位。
- 详细答案:比较各分片操作数、尾延迟、处理器、缓存、磁盘和网络,再按分片键值分布定位大租户、单调键或低基数。只看数据量均匀不足以排除写热点。
- 进阶追问:立刻加分片能解决吗?
- 进阶回答:若键不可拆或查询仍集中,新分片不会自动分流;先证明可迁移数据块和路由形状。
4.3 chunk(数据块)、balancer(均衡器)、jumbo chunk(超大数据块)与 zone(区域)
chunk(数据块)是分片键空间的一段逻辑范围,不是固定物理文件。balancer(均衡器)根据分布策略迁移数据块:目标分片先克隆基线并追赶迁移期间变化,元数据提交成功后路由切到新归属,源端再清理旧范围。迁移会同时消耗源端读取、目标端写入、网络、索引和临时空间。若键分布不可拆、单一键值聚集过多数据,可能形成 jumbo chunk(超大数据块)并阻碍迁移。zone(区域)可把范围约束到指定分片组,但容量不足或规则冲突会让均衡无法完成。
| 现象 | 第一类证据 | 第二类证据 | 处置 |
|---|---|---|---|
| 迁移持续失败 | 均衡器与迁移状态 | 目标磁盘、网络和索引写入 | 限速、扩容、清残留后重试 |
| jumbo chunk(超大数据块) | 数据块大小与可拆点 | 分片键值分布 | 调整键或重新分片 |
| zone(区域)不均 | 区域规则与块归属 | 各区域容量和故障域 | 修正规则并预留余量 |
| 路由元数据陈旧 | 版本冲突与重试 | 配置服务器健康 | 刷新路由并排查控制面 |
flowchart LR
B["balancer 均衡器"] --> S["选择源 chunk 数据块"]
S --> C["向目标分片克隆"]
C --> U["追赶迁移期间更新"]
U --> V{"校验与元数据提交"}
V -->|成功| R["切换路由归属"] --> G["源端清理"]
V -->|失败| K["保持旧归属"]
K --> H{"磁盘、网络或 jumbo?"}
H --> F["止血、修复并重试"]
F -.重新评估.-> B图 14 说明。 节点覆盖数据块选择、克隆、增量追赶、元数据提交和源端清理,箭头明确提交前失败保持旧归属。前提是源目标均有容量且键范围可迁移;正常路径原子切换归属;失败路径因磁盘、网络或超大数据块返回修复。业务结论是迁移先保证路由正确,再追求均衡速度。
热门面试题
- 问题(基础题):chunk(数据块)迁移为何不是简单复制文件?
- 考点:逻辑键范围、增量追赶与元数据切换。
- 回答思路:按基线、增量、提交和清理阶段回答。
- 详细答案:数据块是键范围,目标要克隆对应文档和索引影响,追赶期间更新,再通过集群元数据切换归属,最后清理源范围。任一阶段都可能消耗前台资源。
- 进阶追问:迁移时客户端要停写吗?
- 进阶回答:通常设计为在线追赶,但具体并发语义和限制属于版本敏感项,必须现场压测与核对。
- 问题(原理题):jumbo chunk(超大数据块)为什么难迁移?
- 考点:不可拆键范围与迁移成本。
- 回答思路:说明单键集中和缺少有效拆分点。
- 详细答案:若大量数据集中在同一分片键值或范围内缺少可用拆分点,数据块会过大且无法按常规粒度均衡。根因通常是分片键基数或分布不适合,而不只是均衡器慢。
- 进阶追问:手工多切几次可以吗?
- 进阶回答:同值数据无法靠任意边界拆散,可能需要更换分片键或执行重新分片迁移。
- 问题(场景题):块迁移把磁盘打满如何止血?
- 考点:正确性优先的迁移处置。
- 回答思路:先停或限迁移,再保护副本与业务写入。
- 详细答案:先确认当前归属和提交阶段,暂停或限速均衡,保护目标磁盘水位与副本稳定;再清理失败残留、扩容或调整窗口。不能在归属不明时手工删除源数据。
- 进阶追问:恢复后如何回归?
- 进阶回答:小数据块灰度迁移,验证路由、文档计数、校验摘要、磁盘峰值和业务尾延迟。
5. 维护、项目边界与版本核对
5.1 线上排障、项目建模、设计思想与版本核对卡
线上排障按“业务影响与权威事实 -> 写确认与可见性 -> 查询计划与路由 -> 副本与分片 -> cache(缓存)、磁盘、网络 -> 修复回归”推进。每类事故至少保留两类独立证据:数据库内部状态与主机/业务时间线。WMS(仓储管理系统)商品扩展属性、跨境物流轨迹报文、Runner(执行器)配置和 IoT(物联网)设备元数据适合在访问边界明确时使用文档模型;库存和资金不变量仍必须由条件约束、幂等、transaction(事务)或等价原子边界、流水与对账保障。
| 场景 | 现象与两类证据 | 立即止血 | 长期修复与回归 |
|---|---|---|---|
| cache(缓存)压力/文档搬移 | 驱逐、脏页;扫描与文档尺寸分位数 | 限制大查询和写并发 | 重划文档、索引并压测冷热工作集 |
| 多索引写放大/慢聚合 | 写延迟、索引数;计划与溢写 | 停非关键索引写、限聚合 | 精简索引、前推过滤并回放负载 |
| 复制延迟/频繁选举 | 应用水位、任期;磁盘与网络时间线 | 权威读主、隔离不稳成员 | 修复资源和拓扑并故障注入 |
| 迁移/热点/磁盘水位 | 块状态、目标分片;键分布与主机水位 | 暂停均衡、保护写入 | 重设分片键、扩容并灰度迁移 |
| 字段爆炸/连接池耗尽 | 类型与字段分布;连接等待和请求来源 | 拒绝异常模式、限制新连接 | 校验模式、池预算与容量回归 |
flowchart TD
A["确认业务影响与权威事实"] --> B["写确认、未知结果与可见性"]
B --> C["explain 执行计划与分片路由"]
C --> D["副本延迟、任期与块迁移"]
D --> E["cache 缓存、磁盘、网络、连接池"]
E --> F["止血后根因修复"]
F --> G["故障注入、数据校验与容量回归"]
B -.不变量受损.-> H["冻结写入并对账"]
C -.广播或溢写.-> I["限流与降级查询"]
D -.路由归属不明.-> J["停止迁移并保留旧归属"]图 15 说明。 节点是从业务事实到资源层的排障顺序,箭头包含正常定位、冻结对账、查询降级和停止迁移三条失败路径。前提是时钟和请求标识可关联;正常路径在止血后完成根因与回归;失败路径优先保护不变量和路由归属。业务结论是先保正确,再恢复吞吐,最后用可复现实验证明修复。
项目映射。 WMS(仓储管理系统)商品 document(文档)可嵌入有界扩展属性,报价和库存流水引用权威事实;跨境物流主文档保留最新节点与摘要,原始轨迹按事件拆分;Runner(执行器)配置采用版本 document(文档)加原子激活指针;IoT(物联网)设备元数据使用 schema validation(模式校验)限制关键类型,遥测事件另行保存。以上场景都不允许用“字段灵活”绕过唯一标识、状态机、幂等和对账。
设计思想。 embedding(嵌入)用空间换局部性和单文档原子边界,reference(引用)用 Join(连接)或多次读取成本换共享事实;灵活模式把演进能力交给团队,也把治理责任交给团队;多数确认在延迟、耐久和可用性之间取舍,不代表跨系统完成;分片键是访问模式、热点风险、故障隔离和未来迁移成本的编码。
版本核对卡。 项目版本:待现场核对。学习基线版本:待现场核对。部署形态、存储引擎、驱动版本、副本投票成员、默认读写关注、事务限制、可重试读写、聚合内存与磁盘溢写、TTL(生存时间)调度、均衡器、数据块拆分、重新分片、备份恢复格式均为版本敏感行为。现场必须记录准确版本、配置来源、官方章节、最小复现实验、升级兼容和回退边界;在核对完成前只陈述机制框架,不断言默认值。
热门面试题
- 问题(基础题):MongoDB(文档数据库)最适合哪些项目数据?
- 考点:访问局部性与业务边界。
- 回答思路:列举有界聚合,再给出库存资金反例。
- 详细答案:形状多样、按聚合根整体读取、字段演进频繁且一对多有界的数据更合适,例如商品扩展属性、设备元数据和版本化配置。库存与资金仍要先证明不变量、事务和对账,不因产品类型自动适合。
- 进阶追问:轨迹为什么不是全部嵌入?
- 进阶回答:轨迹无界增长且常按时间独立查询,主文档只留摘要,事件拆分更稳健。
- 问题(原理题):排障为何需要两类证据?
- 考点:相关性与因果链。
- 回答思路:比较数据库内部指标和业务/主机时间线。
- 详细答案:单一指标只能显示相关变化,无法证明原因。例如复制延迟既可能来自网络,也可能来自目标磁盘或应用压力;需要成员水位加主机资源、请求标识和业务结果交叉确认。
- 进阶追问:止血后可以直接结案吗?
- 进阶回答:不可以,还要复现根因、修复模型或容量,并用故障注入和业务校验证明不再发生。
- 问题(场景题):项目版本未知时怎样回答版本敏感问题?
- 考点:事实边界与核对卡。
- 回答思路:先给稳定机制,再列现场核对项和实验。
- 详细答案:明确写“待现场核对”,只解释不依赖默认值的机制链;对驱动重试、事务限制、聚合溢写、均衡和恢复等行为列出版本、配置、官方章节与最小实验,不冒充已知事实。
- 进阶追问:为什么不能说按默认配置?
- 进阶回答:默认值可能随版本、部署方式和托管平台变化,且现场常有覆盖配置,必须以运行证据为准。
6. 综合口述题库
综合问题:如何确定 MongoDB(文档数据库)的 document(文档)边界,并避免 16
MiB(兆字节)限制成为线上事故?- 口述答案:我的结论是先按不变量、同读同写和增长上界划聚合根,再用 16
MiB(兆字节)作为硬门槛前的风险线,而不是把所有相关数据塞进一个 document(文档)。机制上,BSON(二进制 JSON 文档)会重复保存字段名和类型,嵌套对象与数组既占正文,也影响网络、cache(缓存)、更新搬移、索引和复制。以跨境物流为例,主文档 18 KiB(千字节),每条原始报文 2 KiB(千字节),每天 80 条并保留 120 天,新增约 18.75MiB(兆字节),无论当前是否压缩都已证明无界。设计应让主文档只保存运单标识、最新节点、摘要和事件水位,原始轨迹按运单与时间拆为事件文档,附件进入对象存储。失败边界不仅是写入超限,还包括单票读取拖入全部历史、并发追加冲突、多键索引膨胀和故障恢复变慢。项目实施时我会记录文档大小的中位数、高分位数、最大值与日增长,设置提前告警并用极端长生命周期样本压测。回归同时验证主文档原子更新、事件幂等、迟到排序和归档恢复,证明拆分后没有把大小问题变成重复与乱序问题。 进一步落地时,我会先在影子环境固定数据快照、请求参数、并发和资源配额,记录变更前后的扫描量、写入量、复制水位、磁盘峰值与业务结果;再只改变一个关键因素做对照,避免把缓存升温或流量波动误当收益。故障注入必须覆盖网络断开、进程切换、磁盘变慢和重复请求,恢复后按稳定业务键核对数量、状态、版本与流水。发布采用小流量灰度,设置停止条件和回滚点,观察窗口跨过一个完整高峰;监控、处置手册和责任人齐备后才扩大范围。 - 追问:距离 16
MiB(兆字节)还有一半是否安全? - 直接回答:不能只看当前值,还要按增长率、异常报文和索引更新评估到达门槛的时间。
- 追问:压缩能否延后拆分?
- 直接回答:压缩比会随数据变化,且内存中通常按解压形态工作,不能替代有界模型。
- 追问:拆分是否失去原子性?
- 直接回答:跨文档部分不再天然原子,因此主摘要要以事件水位和幂等投影收敛。
- 详情:文档大小边界
- 口述答案:我的结论是先按不变量、同读同写和增长上界划聚合根,再用 16
综合问题:请用 WMS(仓储管理系统)商品说明 embedding(嵌入)和 reference(引用)的取舍。
- 口述答案:我不会从“文档库适合嵌入”出发,而会先问哪些字段与商品同读同写、是否共享、是否有界。商品名称、包装规格和二十多个展示扩展属性通常随商品详情一起读取,变化频率低且数量有上限,适合 embedding(嵌入),可以一次读取并在单 document(文档)内原子修改。供应商报价、仓库库存和历史调价被多个流程共享,更新频繁、数量可能从几十增长到几千,更适合 reference(引用)。例如 100 万商品每个嵌入 24 个 60 B(字节)属性,正文约增加 1.44 KiB(千字节),局部性收益明确;若再嵌入 3000 条 120 B(字节)报价,单商品增加约 352 KiB(千字节)且形成更新热点。可以在商品中冗余“最低展示价”摘要,但必须标出报价集合为权威源,使用幂等事件、版本和水位更新摘要,定期重算校验。失败边界是共享字段被多处直接修改、删除未传播、摘要落后却被用于结算。上线前我会回放商品列表、详情、报价更新和库存裁决四类查询,比较读取次数、文档尺寸、冲突率与写放大;只有展示读取受益且权威不变量未迁移到冗余字段,模型才成立。 工程验证不能只跑一次成功样例。我会准备正常、边界、热点、陈旧和损坏五类数据,分别测冷启动、热稳态与高峰并发,保存查询计划、成员进度、主机资源和业务日志的同一时间线。发生超时先区分明确失败、已经成功与结果未知,再按幂等键收敛,禁止依赖猜测重放。上线先灰度一个租户或一个数据范围,任何不变量差异、尾延迟越线或恢复时长超标都立即回退;最后把复现步骤、阈值和修复证据写入运行手册。
- 追问:引用一定需要 Join(连接)吗?
- 直接回答:不一定,可由应用分步读取、批量查询或预先投影,代价仍需量化。
- 追问:最低价摘要可否用于支付?
- 直接回答:不能直接用于资金裁决,支付必须回到带版本和有效期的权威报价。
- 追问:何时重评嵌入边界?
- 直接回答:当基数、更新频率、生命周期或主要查询形状明显改变时重评。
- 详情:嵌入、引用与冗余
综合问题:灵活模式下如何完成字段演进,而不是让 collection(集合)逐渐失控?
- 口述答案:核心结论是灵活模式只降低一次性变更阻力,不取消契约、兼容和数据质量治理。关键标识、金额、状态、事件时间、数组上限与类型应通过 schema validation(模式校验)和应用校验双层约束,扩展属性则保留受控弹性。字段改名或类型变化不能直接全量覆盖,我采用“扩展读取、写新字段、历史回填、差异校验、收敛读取、停旧写、观察后删除”的状态机。以 500 万台 IoT(物联网)设备为例,固件版本本应为字符串,2% 即 10 万条被写成数值;这会让过滤、排序和索引语义分裂。先阻断新增异型,再引入新字段承载统一类型,按设备型号和标识分批回填,每批记录读取数、修改数、失败数和校验摘要;新旧值差异归零、旧客户端退出并通过回滚演练后才删旧字段。失败边界包括滚动发布期间双字段分叉、批任务覆盖新写、动态字段名爆炸和旧版本回滚读不到值。验证闭环要同时看类型分布、缺失率、拒绝计数、客户端版本占比和代表性查询计划,确保数据、应用与索引一起收敛,而不是只宣布脚本执行成功。 容量评估还要加入增长和故障余量:用高分位文档、最大租户、峰值写率与最长保留期推算半年后的数据、索引、日志、临时空间和网络,而不是按平均值配置。验证时让一个成员落后或一个分片变慢,观察请求是否正确降级,后台任务是否抢占前台资源。恢复后执行文档计数、关键字段类型分布、业务聚合与抽样明细四层校验,并对差异保留可追溯清单。只有容量水位、数据正确性和故障恢复同时达标,方案才可进入生产。
- 追问:为何不在原字段上直接改类型?
- 直接回答:新旧客户端会并存,原地改会产生不可兼容窗口并削弱回滚能力。
- 追问:校验规则越严格越好吗?
- 直接回答:不是,扩展区应保留演进空间,关键业务字段才优先强约束。
- 追问:如何防止回填覆盖新值?
- 直接回答:使用版本条件更新、分批水位和幂等规则,只改仍处于旧版本的记录。
- 详情:字段演进与反模式
综合问题:请解释 WiredTiger(存储引擎)从 B-Tree(平衡树)到 cache(缓存)和磁盘 page(页)的读取路径。
- 口述答案:查询先通过集合或索引的 B-Tree(平衡树)定位键范围,所需磁盘 page(页)以压缩形态保存,读入 cache(缓存)后解压供比较、投影和修改。命中热工作集时主要消耗内存与处理器;未命中时发生磁盘读取,若大扫描把冷 page(页)持续带入 cache(缓存),会驱逐热点并把后续点查变成随机输入输出。更新在内存 page(页)上形成 dirty page(脏页),再由驱逐和 checkpoint(检查点)稳定写出。因此“磁盘文件只有 7 GiB(吉字节)”不能推导只需 7 GiB(吉字节)缓存,内存工作集应按解压形态、索引和并发估算。教学场景中缓存预算 24 GiB(吉字节)、热点数据与索引 18 GiB(吉字节),一个无界聚合又读入 20 GiB(吉字节)冷数据,点查可能由 5 ms(毫秒)升到 80 ms(毫秒)。排障时我会交叉观察缓存占用与 dirty page(脏页)比例、读入/写出字节、驱逐活动、查询形状和磁盘尾延迟。止血先限制扫描和并发,恢复索引剪枝;长期通过投影缩窄、归档、索引精简和容量压测修复。回归必须覆盖冷启动、热稳态和并发大扫描三种阶段,避免只测热缓存得出错误结论。 我还会明确可观测门槛与操作顺序:告警必须能从业务请求标识关联到数据库操作、成员水位、路由目标和主机资源,值班人员先保护权威事实,再限制非关键流量,最后处理容量与性能。任何临时参数调整都记录原值、适用版本、影响范围和恢复时点,避免止血措施永久化。修复后重放事故时间窗的真实负载,比较错误率、尾延迟、未知结果停留、复制追赶与磁盘余量,并由业务聚合和流水对账共同签收。
- 追问:缓存命中高为何仍可能慢?
- 直接回答:锁竞争、处理器、宽文档解码、排序和写出压力仍可抬高延迟。
- 追问:压缩比高是否意味着工作集小?
- 直接回答:不意味着,缓存常承载解压 page(页),还要加上索引和执行中间状态。
- 追问:重启为何不是首选止血?
- 直接回答:它会清空热缓存并丢失现场,可能让恢复阶段延迟更高。
- 详情:存储页与缓存
综合问题:一次多数写从客户端到 Primary(主节点)确认经历哪些阶段?
- 口述答案:我把一次写拆成执行、本地恢复记录、复制历史和客户端承诺四层。请求带业务幂等键到达 Primary(主节点),存储引擎在 cache(缓存)中定位并修改 page(页),按配置写入 journal(预写日志),复制层同时把操作追加到 oplog(操作日志)。Secondary(从节点)拉取并应用 oplog(操作日志),当 write concern(写关注)要求的成员数量和持久化条件满足后,主节点才返回。此时相关数据 page(页)未必已经由 checkpoint(检查点)写回,落后从节点也未必可见,消息消费者、支付渠道和对账更没有自动完成。三成员中主节点本地处理 4 ms(毫秒),较快从节点往返 14 ms(毫秒),多数确认可能约 18 ms(毫秒);另一从节点延迟 220 ms(毫秒)并不阻塞当前多数,但较快从节点故障后确认会跃升。若响应在提交后丢失,客户端面对未知结果,应在新旧主切换稳定后按幂等键查询权威结果,不能把超时当失败。验证时我会记录请求标识、确认配置、成员复制水位、任期与响应时间,注入提交前断连、提交后丢响应和单从节点延迟,证明既不重复执行,也不会把数据库确认误报为跨系统完成。 为了防止局部优化制造新风险,我会把读取收益与写放大、缓存占用、副本延迟、迁移成本和恢复时间放在同一张对照表中。测试数据包含高低选择率、长短文档、大租户和迟到重复事件,压测期间同时执行备份、检查点或受控迁移,观察资源竞争。发布前准备向前兼容和回滚脚本,发布后持续校验权威记录与派生结果;若出现路由扇出、成员落后、磁盘逼近门槛或业务差异,立即停止扩量并按既定状态恢复。
- 追问:数据 page(页)未刷回为何还能确认?
- 直接回答:恢复依赖日志承诺,后台检查点可稍后稳定写出数据页。
- 追问:多数确认是否等待所有成员?
- 直接回答:不是,等待满足配置的多数条件,具体成员和持久化语义现场核对。
- 追问:写成功后能立刻从任意从节点读到吗?
- 直接回答:不能,成员应用进度不同,还需读取关注、会话和路由约束。
- 详情:写入确认链
综合问题:journal(预写日志)、checkpoint(检查点)和 oplog(操作日志)怎样共同支持崩溃恢复?
- 口述答案:三者职责不同:checkpoint(检查点)形成数据文件的恢复起点,journal(预写日志)保存检查点之后本节点恢复所需变化,oplog(操作日志)则让副本集成员追赶并对齐胜出的复制历史。节点崩溃重启时先装载最近一致检查点,再重放 journal(预写日志)恢复本地已承诺变化;回到副本集后还要比较任期和 oplog(操作日志)水位,继续追赶。如果节点离线时间超过可用 oplog(操作日志)窗口,或旧主产生了未获胜历史接受的分叉写,就可能需要重新同步或 rollback(回滚),不能看到进程启动便恢复读流量。假设日志容量可保留 900 GiB(吉字节),平时写率 15
MiB/s(兆字节每秒)约有 17 小时窗口,峰值 60MiB/s(兆字节每秒)只剩约 4.3 小时;离线 6 小时在平时能追赶,峰值就可能超窗。恢复验证必须记录最后检查点、日志连续性、应用水位、重同步耗时和回滚记录,再用业务键核对订单、库存流水与配置版本。独立备份仍不可省,因为副本和日志可能同步逻辑删除或同时损坏。最终以业务查询、数据摘要、副本追平和故障演练时长共同判定恢复完成。 进一步落地时,我会先在影子环境固定数据快照、请求参数、并发和资源配额,记录变更前后的扫描量、写入量、复制水位、磁盘峰值与业务结果;再只改变一个关键因素做对照,避免把缓存升温或流量波动误当收益。故障注入必须覆盖网络断开、进程切换、磁盘变慢和重复请求,恢复后按稳定业务键核对数量、状态、版本与流水。发布采用小流量灰度,设置停止条件和回滚点,观察窗口跨过一个完整高峰;监控、处置手册和责任人齐备后才扩大范围。 - 追问:oplog(操作日志)越大越好吗?
- 直接回答:更大窗口提高追赶余量,但占空间并增加规划成本,应按峰值写率和维修时长计算。
- 追问:副本能否替代备份?
- 直接回答:不能,误删、逻辑错误和权限破坏会复制到副本,仍需隔离备份与恢复演练。
- 追问:回滚记录为何要映射业务键?
- 直接回答:数据库历史对齐后,业务仍需判断哪些订单或流水要重放、补偿或人工对账。
- 详情:崩溃与复制恢复
- 口述答案:三者职责不同:checkpoint(检查点)形成数据文件的恢复起点,journal(预写日志)保存检查点之后本节点恢复所需变化,oplog(操作日志)则让副本集成员追赶并对齐胜出的复制历史。节点崩溃重启时先装载最近一致检查点,再重放 journal(预写日志)恢复本地已承诺变化;回到副本集后还要比较任期和 oplog(操作日志)水位,继续追赶。如果节点离线时间超过可用 oplog(操作日志)窗口,或旧主产生了未获胜历史接受的分叉写,就可能需要重新同步或 rollback(回滚),不能看到进程启动便恢复读流量。假设日志容量可保留 900 GiB(吉字节),平时写率 15
综合问题:write concern(写关注)、read concern(读关注)和 read preference(读偏好)应如何组合?
- 口述答案:组合必须从业务的耐久、可见、延迟和可用性目标反推。write concern(写关注)回答写到何种成员与持久化程度才返回;read concern(读关注)回答允许读取哪层确认历史;read preference(读偏好)只决定优先把读发给哪类成员。库存裁决和配置发布后的确认读通常优先权威 Primary(主节点),并保持会话水位;报表或设备列表若可接受几秒陈旧,可以读 Secondary(从节点),但响应要携带更新时间或延迟指标。三成员中从节点 A 往返 14 ms(毫秒)、B 为 220 ms(毫秒),多数写平时受 A 约束;A 故障后为了维持同样耐久承诺,延迟可能升到 220 ms(毫秒),两个从节点都不可达时写可用性下降。这是明确取舍,不能在故障中静默降低写关注。majority commit point(多数提交点)也不表示所有成员已应用,读到落后 B 仍可能陈旧。上线前要建立场景矩阵,分别列库存扣减、商品展示、轨迹查询和后台分析的允许陈旧、超时和降级路径。回归通过单成员故障、网络延迟和读路由漂移验证:关键读不倒退,非关键读在预算内,配置变更有审计,跨系统完成另由事件水位和对账判断。 工程验证不能只跑一次成功样例。我会准备正常、边界、热点、陈旧和损坏五类数据,分别测冷启动、热稳态与高峰并发,保存查询计划、成员进度、主机资源和业务日志的同一时间线。发生超时先区分明确失败、已经成功与结果未知,再按幂等键收敛,禁止依赖猜测重放。上线先灰度一个租户或一个数据范围,任何不变量差异、尾延迟越线或恢复时长超标都立即回退;最后把复现步骤、阈值和修复证据写入运行手册。
- 追问:多数写配从节点读是否天然一致?
- 直接回答:不天然一致,从节点可能尚未应用,需要会话、读取关注或回主策略。
- 追问:故障时可否自动降低写关注?
- 直接回答:这会改变耐久承诺,必须由业务预案显式批准并保留审计。
- 追问:读偏好能保证低延迟吗?
- 直接回答:不能,成员负载、网络、复制延迟和查询计划都会影响实际延迟。
- 详情:一致性控制
综合问题:会话因果一致性和可重试写怎样处理主节点切换中的未知结果?
- 口述答案:会话因果一致性负责传播操作先后水位,可重试写负责在支持范围内识别同一数据库操作的网络重发,业务幂等负责把同一业务意图收敛到唯一结果,三者是叠加关系。典型故障是应用向旧 Primary(主节点)提交写入,数据库已达到承诺但响应丢失,随后发生选举。客户端不能把超时判成失败,也不能从落后从节点查空后重做;它应保留会话和业务请求标识,等待驱动发现新主,在新 Primary(主节点)上查询幂等记录。若记录存在就返回原结果,明确未提交才重试,历史尚未收敛则进入“结果未知”状态并触发查单与对账。Runner(执行器)配置发布可用发布号作幂等键,支付动作则用支付单号加动作类型,并让幂等记录与权威状态同一原子边界保存。可重试写无法覆盖消息消费、外部支付和人工补单,会话也不会自动跨多个服务传播。验证要注入提交前断连、提交后丢响应、选举中重试、会话丢失和重复回调,统计幂等命中、未知状态停留和业务差额。只有结果可解释且无重复副作用,才算切换恢复完成。 容量评估还要加入增长和故障余量:用高分位文档、最大租户、峰值写率与最长保留期推算半年后的数据、索引、日志、临时空间和网络,而不是按平均值配置。验证时让一个成员落后或一个分片变慢,观察请求是否正确降级,后台任务是否抢占前台资源。恢复后执行文档计数、关键字段类型分布、业务聚合与抽样明细四层校验,并对差异保留可追溯清单。只有容量水位、数据正确性和故障恢复同时达标,方案才可进入生产。
- 追问:延长超时能消除未知结果吗?
- 直接回答:不能,只能减少部分误超时,网络和进程边界仍会产生未知。
- 追问:可重试写等于业务幂等吗?
- 直接回答:不等于,它只覆盖支持的数据库操作和历史窗口,业务副作用范围更大。
- 追问:为什么查询新主而非从节点?
- 直接回答:从节点可能陈旧,查空会诱发重复执行,权威判定应基于新主和有效历史。
- 详情:会话、重试与未知结果
综合问题:多文档 transaction(事务)何时必要,何时应改造聚合边界?
- 口述答案:我先判断多个 document(文档)是否共同构成不可拆的不变量。库存数量与同库权威流水若必须同时成立,可在短小、冲突可控的 transaction(事务)中原子更新;但跨仓批量调整、数千配置同时发布或外部支付不能因为有事务就无限扩大边界。多文档事务会增加提交协调、锁冲突、cache(缓存)占用、日志量和故障重试成本,跨热点分片时风险更高。Runner(执行器)若一次修改 5000 个配置,每 500 个一批且单批持续 8 s(秒),故障会放大重试与资源持有;更好的模型是写入不可变配置版本,逐条校验完成后,用一个很小的原子操作切换“激活版本”指针。若确实要分批修改,则每批 50 个、约 300 ms(毫秒),使用稳定幂等键和可恢复水位。事务提交响应丢失仍是未知结果,应用要查询事务对应业务版本,不能重复生成新版本。验证闭环包括并发冲突、主节点切换、超时、事务重试、缓存压力和旧版本回滚,并检查所有读取者是否只消费完整激活版本。结论不是拒绝事务,而是让事务保护真正的不变量,把批处理和跨系统完成交给状态机、事件与对账。 我还会明确可观测门槛与操作顺序:告警必须能从业务请求标识关联到数据库操作、成员水位、路由目标和主机资源,值班人员先保护权威事实,再限制非关键流量,最后处理容量与性能。任何临时参数调整都记录原值、适用版本、影响范围和恢复时点,避免止血措施永久化。修复后重放事故时间窗的真实负载,比较错误率、尾延迟、未知结果停留、复制追赶与磁盘余量,并由业务聚合和流水对账共同签收。
- 追问:单文档原子是否足够处理所有库存?
- 直接回答:不一定,跨仓、流水和预占关系可能跨文档,需按权威不变量决定。
- 追问:配置版本切换为何更轻?
- 直接回答:大量内容可离线准备,最终只原子修改一个小指针,失败范围清晰。
- 追问:事务能覆盖消息发送吗?
- 直接回答:不能天然覆盖外部系统,应使用可重放事件、幂等消费和对账。
- 详情:多文档事务边界
综合问题:如何从查询形状设计复合索引,并正确使用 ESR(相等排序范围)原则?
- 口述答案:我先收集真实查询的过滤操作符、排序、投影、分页、返回量和参数分布,再把能复用的 equality(相等)字段作为候选前缀,接着考虑 sort(排序)键能否提供稳定顺序,最后放 range(范围)条件,这就是 ESR(相等排序范围)的基本思路。但它只是启发式:字段选择率、分片键路由、数组、多种排序和索引复用可能改变顺序。Runner(执行器)有 1000 万任务,
status=SUCCESS占 92%,单列状态索引会扫描约 920 万键并大量回表;若查询是tenantId=42 AND status=FAILED,该租户 10 万任务且失败率 0.5%,tenantId + status + createdAt复合索引只定位约 500 条并按时间返回。设计后要用代表性成功、失败、大租户和小租户参数运行explain(执行计划),比较扫描键数、读取文档数、返回数、阻塞排序和分片目标数。失败边界包括缺少索引前缀、范围过早截断后续排序利用、低选择率回表和索引过宽造成写放大。回归还要观察写入延迟、cache(缓存)占用和其他查询计划,不能以某一条查询加速为理由无限叠加索引。 为了防止局部优化制造新风险,我会把读取收益与写放大、缓存占用、副本延迟、迁移成本和恢复时间放在同一张对照表中。测试数据包含高低选择率、长短文档、大租户和迟到重复事件,压测期间同时执行备份、检查点或受控迁移,观察资源竞争。发布前准备向前兼容和回滚脚本,发布后持续校验权威记录与派生结果;若出现路由扇出、成员落后、磁盘逼近门槛或业务差异,立即停止扩量并按既定状态恢复。 - 追问:相等字段中选择率最高的一定放最前吗?
- 直接回答:不一定,还要看查询复用、分片键前缀、排序和索引总数。
- 追问:用了索引为何扫描仍很大?
- 直接回答:索引条件可能低选择率或缺前缀,命中索引不代表剪枝有效。
- 追问:怎样证明索引同时支持排序?
- 直接回答:执行计划中应避免额外阻塞排序,并用不同参数验证顺序与方向。
- 详情:查询形状与复合索引
- 综合问题:如何选择唯一、部分、稀疏、
TTL(生存时间)、哈希和通配索引?
- 口述答案:选择索引的起点是要表达的约束与查询,不是功能清单。unique
index(唯一索引)适合稳定业务键,但要明确缺失、空值和组合键语义;partialindex(部分索引)适合只查询未完成任务或有效配置,让小比例活跃数据进入索引;sparseindex(稀疏索引)围绕字段存在性,不能与部分条件混讲。TTLindex(生存时间索引)负责后台过期清理,执行并非精确业务时刻,不能承担订单关单。hashedindex(哈希索引)可打散高基数键,却牺牲原值范围局部性;wildcardindex(通配索引)便于探索动态属性,但会扩大索引体积与治理范围。比如 5000 万任务只有 1% 处于未完成状态,部分索引可能只维护约 50 万条;若业务查询未包含可证明的部分条件,优化器不能冒险使用它。实施时先盘点查询覆盖率、字段缺失率、基数、更新频率与保留期,再为每个索引写明服务对象和退出条件。验证同时比较读扫描量、写延迟、索引尺寸、缓存占用和过期删除峰值。失败边界是把唯一索引误当跨文档业务约束、把过期清理当定时器,或用通配索引掩盖字段爆炸;修复要回到模式和访问边界。 进一步落地时,我会先在影子环境固定数据快照、请求参数、并发和资源配额,记录变更前后的扫描量、写入量、复制水位、磁盘峰值与业务结果;再只改变一个关键因素做对照,避免把缓存升温或流量波动误当收益。故障注入必须覆盖网络断开、进程切换、磁盘变慢和重复请求,恢复后按稳定业务键核对数量、状态、版本与流水。发布采用小流量灰度,设置停止条件和回滚点,观察窗口跨过一个完整高峰;监控、处置手册和责任人齐备后才扩大范围。 - 追问:唯一索引能防止资金重复扣款吗?
- 直接回答:只能约束所定义键唯一,还需状态迁移、幂等结果和渠道对账。
- 追问:部分索引为何有时不被采用?
- 直接回答:查询谓词若不能证明结果属于索引子集,使用它可能漏数据。
- 追问:
TTL(生存时间)删除慢怎么办? - 直接回答:先核对版本行为、删除积压和磁盘压力,业务定时动作不能依赖其精确时刻。
- 详情:索引类型边界
- 综合问题:数组字段建立 multi
keyindex(多键索引)后为什么会退化?
- 口述答案:multi
keyindex(多键索引)把数组元素展开成多个索引键,收益是可按元素定位,代价是文档数不再等于索引项数。200 万设备平均 40 个标签,理论上约产生 8000 万索引键;标签新增、删除或重排会维护相关键,多个索引叠加后写放大、cache(缓存)占用和复制流量都上升。若标签本身低选择率,例如 70% 设备都有“在线”,索引仍要扫描大量键并回表;若查询还要求同一数组元素内多个条件,必须准确表达元素关联,不能把跨元素命中误判为同一元素满足。覆盖查询也会受数组展开和返回语义限制。设计前应统计数组长度的中位数、高分位数、最大值、元素基数和更新频率,给元素数设置模式上限。若 95% 查询只使用 6 个受控标签,可提取固定字段或建立独立映射,减少万能数组。排障用explain(执行计划)比较扫描键、去重、回表和返回量,再结合索引尺寸与写延迟。回归要覆盖空数组、超长数组、重复元素和并发更新,证明查询语义正确且成本在预算内,而不是只看索引已创建。 工程验证不能只跑一次成功样例。我会准备正常、边界、热点、陈旧和损坏五类数据,分别测冷启动、热稳态与高峰并发,保存查询计划、成员进度、主机资源和业务日志的同一时间线。发生超时先区分明确失败、已经成功与结果未知,再按幂等键收敛,禁止依赖猜测重放。上线先灰度一个租户或一个数据范围,任何不变量差异、尾延迟越线或恢复时长超标都立即回退;最后把复现步骤、阈值和修复证据写入运行手册。 - 追问:数组越长只影响磁盘吗?
- 直接回答:还影响缓存、更新、复制、扫描去重和文档大小。
- 追问:多键索引可以覆盖查询吗?
- 直接回答:存在语义与组合限制,必须以目标版本计划和实际投影验证。
- 追问:怎样发现无界数组?
- 直接回答:持续监控数组长度分位数、最大值和单位时间增长,并设置写入上限。
- 详情:多键索引退化
- 综合问题:covered query(覆盖查询)和索引排序怎样减少读取,边界是什么?
- 口述答案:covered query(覆盖查询)的目标是让过滤与投影都从索引项完成,避免读取完整 document(文档);索引排序则让结果按键顺序流出,避免 blocking sort(阻塞排序)。两者都依赖查询形状与索引前缀,不能只把返回字段追加到索引尾部。若查询按租户和状态过滤、按创建时间倒序,只返回任务标识与时间,复合索引可同时剪枝、排序和投影;但若状态占 92%,即便覆盖也可能扫描数百万键。索引越宽,写入、cache(缓存)和复制成本越高,字段更新也要维护更多条目。数组字段、缺失值、排序方向和范围条件还可能让覆盖或顺序失效。验证时用
explain(执行计划)确认读取文档数是否为零或显著下降、是否存在额外排序,并比较扫描键数与返回数;再在冷缓存和写入并发下测尾延迟。失败边界是为列表覆盖加入大字段导致索引膨胀,或只优化一个参数让其他租户计划退化。回归要比较索引前后磁盘尺寸、写吞吐、检查点压力与代表性查询,并为索引记录服务查询和删除门槛。 容量评估还要加入增长和故障余量:用高分位文档、最大租户、峰值写率与最长保留期推算半年后的数据、索引、日志、临时空间和网络,而不是按平均值配置。验证时让一个成员落后或一个分片变慢,观察请求是否正确降级,后台任务是否抢占前台资源。恢复后执行文档计数、关键字段类型分布、业务聚合与抽样明细四层校验,并对差异保留可追溯清单。只有容量水位、数据正确性和故障恢复同时达标,方案才可进入生产。 - 追问:读取文档数为零就一定快吗?
- 直接回答:不一定,低选择率仍可能扫描大量索引键,路由也可能跨多个分片。
- 追问:所有返回字段都应放进索引吗?
- 直接回答:不应,宽索引的写入与缓存成本可能高于减少回表的收益。
- 追问:排序方向如何验证?
- 直接回答:用真实复合条件和正反向需求检查计划是否出现阻塞排序。
- 详情:覆盖与排序
- 综合问题:为什么深分页会慢,如何设计稳定的游标分页?
- 口述答案:偏移分页的问题是数据库仍要定位、扫描并丢弃前面的结果,页码越深成本越高。3000 万订单按创建时间倒序、每页 50 条,第 20 万页前面约有 1000 万条;即使索引提供顺序,也要越过大量键。游标分页应把上一页最后一条的稳定排序键带入下一次范围条件,例如
(createdAt,_id),时间提供业务顺序,唯一标识打破同一时间值的并列。下一页从该复合位置继续,扫描量接近页大小而非页码深度。设计还要明确数据在翻页期间新增、删除和更新时的语义:列表浏览通常接受快照外的变化,但不能重复或遗漏已存在顺序;严格快照则需要额外一致性边界和更高成本。索引顺序必须与租户过滤、时间方向和唯一键匹配,分片场景还要看是否能定向,否则每页仍会全分片归并。验证使用首页、中间页、极深页和大量同时间戳数据,统计扫描键、返回数、分片目标与延迟。失败边界包括只用非唯一时间游标、客户端篡改游标和排序字段被更新;应签名游标并选择不可变排序键。 我还会明确可观测门槛与操作顺序:告警必须能从业务请求标识关联到数据库操作、成员水位、路由目标和主机资源,值班人员先保护权威事实,再限制非关键流量,最后处理容量与性能。任何临时参数调整都记录原值、适用版本、影响范围和恢复时点,避免止血措施永久化。修复后重放事故时间窗的真实负载,比较错误率、尾延迟、未知结果停留、复制追赶与磁盘余量,并由业务聚合和流水对账共同签收。 - 追问:游标分页能直接跳到第十万页吗?
- 直接回答:它擅长连续浏览,不适合任意页跳转;产品交互应改为筛选或有限页码。
- 追问:只用
_id可以吗? - 直接回答:要看业务排序是否与该标识一致,不能默认代表事件时间。
- 追问:分片后为何还可能慢?
- 直接回答:缺少分片键会让各分片分别取数再由路由层归并。
- 详情:分页路径
- 综合问题:如何治理 aggregation pipeline(聚合管道)的内存与磁盘溢写?
- 口述答案:聚合治理的第一原则是尽早减少进入昂贵阶段的数据,而不是先提高内存。管道应前推可用索引的过滤,尽早投影掉大字段,在分组和排序前限制时间、租户与状态范围,并估算分组基数。2 亿条设备事件按设备分组,若有 500 万设备、每组状态教学估值 80 B(字节),仅状态约 381
MiB(兆字节),实际哈希结构、键和值还会更大,容易触发 disk spill(磁盘溢写)或失败。允许溢写只是把内存风险转成临时磁盘、输入输出和延迟风险,具体阈值与默认行为属于版本敏感项,必须写入核对卡。排障先看分片扇出、各阶段输入输出数、阻塞排序、分组基数、溢写字节和磁盘水位,再识别是否有无界时间范围或字段类型漂移。止血可限制并发、缩短时间窗、返回预聚合结果;长期建立小时汇总、合理索引和容量预算。回归要在磁盘低水位、并发聚合与块迁移同时发生的压力下验证,确保查询能降级且不挤占主业务写入,并记录证据。 为了防止局部优化制造新风险,我会把读取收益与写放大、缓存占用、副本延迟、迁移成本和恢复时间放在同一张对照表中。测试数据包含高低选择率、长短文档、大租户和迟到重复事件,压测期间同时执行备份、检查点或受控迁移,观察资源竞争。发布前准备向前兼容和回滚脚本,发布后持续校验权威记录与派生结果;若出现路由扇出、成员落后、磁盘逼近门槛或业务差异,立即停止扩量并按既定状态恢复。 - 追问:开启磁盘溢写是否就安全?
- 直接回答:不安全,它可能打满临时磁盘并拖慢复制、迁移和其他查询。
- 追问:过滤写在管道前面就一定前推吗?
- 直接回答:仍需看表达式、索引和实际执行计划,文本位置不能代替证据。
- 追问:何时做预聚合?
- 直接回答:当固定窗口和维度被高频重复计算,且迟到修正与重建机制明确时。
- 详情:聚合与溢写
- 综合问题:如何用 explain(执行计划)形成慢查询证据闭环?
- 口述答案:我不会只看“使用了某索引”,而是从路由、候选计划、扫描、回表、排序、聚合到返回逐层核对。第一步固定代表性参数、时间窗和数据量,记录查询形状;第二步看 mongos(路由进程)命中了几个分片,定向与广播的成本先分开;第三步比较扫描键数、读取 document(文档)数和返回数,若三者比例相差数千倍,说明剪枝或过滤有问题;第四步检查 blocking sort(阻塞排序)、分组基数和 disk spill(磁盘溢写);最后结合缓存冷热、并发和磁盘尾延迟解释实际耗时。1000 万任务中成功状态占 92%,状态索引被选中并不代表高效,扫描 920 万键返回 50 条仍是退化。修复可能是增加租户前缀、调整复合索引、游标分页、缩短范围或重写管道,但每次只改变一个关键因素并对比计划。失败边界是只用小租户参数、只在热缓存测试,或强制计划后数据分布改变。回归要覆盖高低选择率、冷热缓存、单分片与广播,并观察写放大和其他查询,确保局部优化没有制造全局回归。 进一步落地时,我会先在影子环境固定数据快照、请求参数、并发和资源配额,记录变更前后的扫描量、写入量、复制水位、磁盘峰值与业务结果;再只改变一个关键因素做对照,避免把缓存升温或流量波动误当收益。故障注入必须覆盖网络断开、进程切换、磁盘变慢和重复请求,恢复后按稳定业务键核对数量、状态、版本与流水。发布采用小流量灰度,设置停止条件和回滚点,观察窗口跨过一个完整高峰;监控、处置手册和责任人齐备后才扩大范围。
- 追问:扫描键数接近返回数就一定正常吗?
- 直接回答:还要看回表文档、排序、分片扇出、文档宽度和等待时间。
- 追问:能否直接固定某个索引?
- 直接回答:固定路径可能随分布变化失效,应先修正统计、查询与索引并设回归监控。
- 追问:为什么要区分冷热缓存?
- 直接回答:热缓存会隐藏随机读和工作集超预算,线上故障常发生在缓存被扰动后。
- 详情:执行计划证据
- 综合问题:副本集如何选举,未多数提交的写为什么可能 rollback(回滚)?
- 口述答案:副本集通过心跳和多数投票选择新的 Primary(主节点),term(任期)标识领导历史,候选成员还要具备足够新的 oplog(操作日志)。旧主失去多数后不能继续作为有效主提供承诺;如果它曾接受但没有进入多数胜出历史的写,重新加入时必须对齐新主并 rollback(回滚)分叉。客户端在切换期间可能看到连接错误或结果未知,驱动负责重新发现拓扑,业务则按幂等键查询新主,不能在从节点查空后重复执行。假设操作 X 只存在旧主,随后另外两个成员组成任期 42 并选出新主,X 不属于胜出历史,旧主回归会回滚;若 X 已达到多数提交,则新主候选应拥有该历史。排障要把任期变化、成员投票、心跳、应用水位、回滚记录与请求时间线对齐。止血时隔离不稳定成员、限制非关键写并保护权威读;修复网络、磁盘或进程暂停后,再注入单节点故障验证选举时间、未知结果率和幂等命中。最终还要把回滚记录映射到订单、库存与配置业务键,完成重放、补偿或人工对账。 工程验证不能只跑一次成功样例。我会准备正常、边界、热点、陈旧和损坏五类数据,分别测冷启动、热稳态与高峰并发,保存查询计划、成员进度、主机资源和业务日志的同一时间线。发生超时先区分明确失败、已经成功与结果未知,再按幂等键收敛,禁止依赖猜测重放。上线先灰度一个租户或一个数据范围,任何不变量差异、尾延迟越线或恢复时长超标都立即回退;最后把复现步骤、阈值和修复证据写入运行手册。
- 追问:任期增加是否一定异常?
- 直接回答:计划切换也会增加,异常要结合频率、原因和业务错误判断。
- 追问:多数写能否完全消除回滚?
- 直接回答:能显著收窄正常选举下的风险,但要按实际配置和拓扑核对,未多数写仍有窗口。
- 追问:新主可用后为何还要查回滚?
- 直接回答:集群可用不代表旧主分叉业务已补偿,业务结果仍需解释。
- 详情:选举与回滚
- 综合问题:如何处理陈旧读、读己之写和故障切换后的事务恢复?
- 口述答案:先把三件事分开:陈旧读是目标成员应用水位落后,读己之写要求后续读至少观察到本会话前序写,事务恢复则要判断提交是否已经进入有效历史。普通商品展示可接受几秒陈旧时,可用 Secondary(从节点)并返回数据时间;库存裁决、支付状态和刚发布配置的确认读应走 Primary(主节点)或带会话因果水位的兼容读。主节点切换后,客户端保留会话、事务标识和业务幂等键,在新主查询提交结果;已提交返回原结果,明确未提交才重试,仍未知则进入查单对账。不能用固定睡眠等待,因为复制延迟会从毫秒变成分钟;也不能仅凭从节点“连接正常”推导数据已追平。证据包括 majority commit point(多数提交点)、各成员应用水位、选点日志、会话时间和业务版本。止血可把关键读临时回主、对非关键读标陈旧并关闭跨页强一致假设。回归注入从节点延迟、选举和事务响应丢失,验证读值不倒退、重复请求返回同一业务结果、未知状态最终收敛,且降级流量不会压垮新主。 容量评估还要加入增长和故障余量:用高分位文档、最大租户、峰值写率与最长保留期推算半年后的数据、索引、日志、临时空间和网络,而不是按平均值配置。验证时让一个成员落后或一个分片变慢,观察请求是否正确降级,后台任务是否抢占前台资源。恢复后执行文档计数、关键字段类型分布、业务聚合与抽样明细四层校验,并对差异保留可追溯清单。只有容量水位、数据正确性和故障恢复同时达标,方案才可进入生产。
- 追问:读偏好设主节点就绝对最新吗?
- 直接回答:它减少副本陈旧窗口,但仍需考虑并发顺序、事务读取语义和客户端会话。
- 追问:因果一致性会自动跨服务吗?
- 直接回答:不会,逻辑会话和时间上下文必须显式传播且受驱动能力约束。
- 追问:事务提交超时可以直接重开事务吗?
- 直接回答:不可以,先确认原事务结果,否则可能重复业务副作用。
- 详情:一致性与故障恢复
- 综合问题:怎样规划 oplog(操作日志)窗口并治理复制延迟?
- 口述答案:oplog(操作日志)窗口要按峰值写率、最长故障维修时间、追赶速度和安全余量计算,不能只看平时“还能保留几天”。900 GiB(吉字节)日志在 15
MiB/s(兆字节每秒)下约有 17 小时,峰值 60MiB/s(兆字节每秒)时只剩约 4.3 小时;从节点离线 6 小时就可能从可追赶变为重新同步。复制延迟还要拆成拉取落后、持久化落后和应用落后:网络拥塞主要影响获取,目标磁盘与多索引写放大影响持久化,大事务、热点更新和资源争用影响应用。排障用成员水位与主机网络、磁盘尾延迟、cache(缓存)压力和前台写率交叉,不能只看一个“延迟秒数”。止血可限制大批写、迁移和聚合,保护至少一个可追赶从节点,并将关键读回主;若已超窗,规划重新同步容量,避免同时失去多个副本。长期按峰值重算日志容量、隔离磁盘争用并建立离线时长告警。回归在高峰写率下让单成员离线并恢复,验证追赶时间、前台延迟、磁盘峰值和多数可用性。 我还会明确可观测门槛与操作顺序:告警必须能从业务请求标识关联到数据库操作、成员水位、路由目标和主机资源,值班人员先保护权威事实,再限制非关键流量,最后处理容量与性能。任何临时参数调整都记录原值、适用版本、影响范围和恢复时点,避免止血措施永久化。修复后重放事故时间窗的真实负载,比较错误率、尾延迟、未知结果停留、复制追赶与磁盘余量,并由业务聚合和流水对账共同签收。 - 追问:复制延迟为零就没有风险吗?
- 直接回答:不代表备份有效、业务对账完成或故障时仍有足够日志窗口。
- 追问:增加日志容量能修复磁盘慢吗?
- 直接回答:只能增加追赶余量,不能消除持续的写出和应用瓶颈。
- 追问:何时需要重新同步?
- 直接回答:当成员无法从现存有效历史连续追赶时,按目标版本流程重建并校验。
- 详情:日志追赶窗口
- 综合问题:怎样为多租户业务选择 shard
key(分片键)?
- 口述答案:分片键要同时回答写入是否均匀、主要查询能否定向、单租户能否拆分、区域与故障隔离如何实现,以及未来重新分片要搬多少数据。只用
tenantId能让租户查询定向,但超大租户全部落在一个键值范围,形成热点和 jumbo chunk(超大数据块);只用时间会把新写集中到最新范围;只用随机哈希能打散写,却让按时间范围查询扇出。常见折中是根据工作负载组合租户、哈希桶、业务标识或时间,例如主查询按租户和运单点查,可让复合键保留定向能力,并用足够高基数的后缀拆大租户。设计时拿最大租户、典型租户和长尾租户分别演绎写率、数据量、查询比例和增长。还要统计缺少分片键的更新与聚合,它们可能广播到所有分片。上线前做路由回放,计算定向率、每分片写率、高分位延迟、数据块尺寸和迁移量;再模拟新增分片与大租户增长。失败边界是数据量均匀但请求不均、键值不可拆或区域规则容量不足。结论是分片键不是存储字段选择,而是把访问模式与迁移成本固化进集群。 为了防止局部优化制造新风险,我会把读取收益与写放大、缓存占用、副本延迟、迁移成本和恢复时间放在同一张对照表中。测试数据包含高低选择率、长短文档、大租户和迟到重复事件,压测期间同时执行备份、检查点或受控迁移,观察资源竞争。发布前准备向前兼容和回滚脚本,发布后持续校验权威记录与派生结果;若出现路由扇出、成员落后、磁盘逼近门槛或业务差异,立即停止扩量并按既定状态恢复。 - 追问:高基数字段一定是好分片键吗?
- 直接回答:不一定,还要看查询是否携带、写入分布、范围需求和大租户拆分。
- 追问:分片后可以随时轻松改键吗?
- 直接回答:改键涉及版本能力、全量迁移、容量和回滚,必须作为高成本变更规划。
- 追问:数据均匀为何仍会热点?
- 直接回答:请求可能集中在少量活跃键,容量均匀不代表操作率均匀。
- 详情:分片键与路由
- 综合问题:hashed sharding(哈希分片)与 ranged sharding(范围分片)如何取舍?
- 口述答案:hashed sharding(哈希分片)把高基数原值映射到分散的哈希区间,适合标识点查和打散单调写入;代价是相邻原值不再相邻,时间或编号范围查询可能访问多个分片。ranged sharding(范围分片)保留原值顺序,适合按租户、地域或时间范围定向,也便于 zone(区域)放置;代价是单调增长会把新写压到边缘数据块,低基数和大租户会形成热点。选择时必须量化点查、范围查、写入键分布与区域约束。例如跨境轨迹 90% 按运单标识点查、10% 按租户时间窗扫描,可考虑租户前缀加高基数后缀,避免只哈希时间导致范围全扇出,也避免只按租户让大客户不可拆。验证用历史请求回放计算定向率、每分片写率、数据块增长和范围查询目标数;再注入单一大租户与单调键峰值。失败边界包括路由均匀却范围查询网络爆炸,或范围局部却最新块持续过热。最终方案要同时写出不适用查询、扩容迁移量与重新分片退路,不能只凭“哈希更均匀”下结论。 进一步落地时,我会先在影子环境固定数据快照、请求参数、并发和资源配额,记录变更前后的扫描量、写入量、复制水位、磁盘峰值与业务结果;再只改变一个关键因素做对照,避免把缓存升温或流量波动误当收益。故障注入必须覆盖网络断开、进程切换、磁盘变慢和重复请求,恢复后按稳定业务键核对数量、状态、版本与流水。发布采用小流量灰度,设置停止条件和回滚点,观察窗口跨过一个完整高峰;监控、处置手册和责任人齐备后才扩大范围。
- 追问:时间键适合单独做范围分片吗?
- 直接回答:通常有最新范围写热点,除非写入和分区策略已专门验证。
- 追问:哈希后还能按原值范围剪枝吗?
- 直接回答:原值相邻会被打散,范围局部性通常减弱,需看复合键与查询。
- 追问:如何证明方案可扩容?
- 直接回答:新增分片后实测数据块可拆、可迁移且流量随路由重新分布。
- 详情:哈希与范围分片
- 综合问题:mongos(路由进程)的定向查询与广播查询成本有何差异?
- 口述答案:定向查询从谓词提取完整或可剪枝的 shard
key(分片键),结合 config server(配置服务器)的 chunk(数据块)范围与版本,只把请求发到目标 shard(分片);广播查询无法确定归属,要向多个分片执行,再在 mongos(路由进程)协调合并、排序或聚合。8 个分片各有 5000 万轨迹,按租户和运单查询一票可只访问 1 个分片并扫描约 30 条;仅按运输状态查询会扇出 8 个分片,若各返回 10 万候选,协调层要处理 80 万条。并发 100 个同类请求会把分片内尚可接受的查询放大成网络、内存和连接池事故。排障要先看目标分片数,再看每个分片的索引与扫描,不能只优化分片内索引。止血可限制无界状态查询、缩短时间窗、返回异步报表或预聚合结果;长期让高频查询携带路由字段,或建立面向该查询的派生模型。回归同时测单分片、部分分片和全分片,在一个慢分片情况下验证超时与降级,确保协调层不会无限等待或堆积结果。 工程验证不能只跑一次成功样例。我会准备正常、边界、热点、陈旧和损坏五类数据,分别测冷启动、热稳态与高峰并发,保存查询计划、成员进度、主机资源和业务日志的同一时间线。发生超时先区分明确失败、已经成功与结果未知,再按幂等键收敛,禁止依赖猜测重放。上线先灰度一个租户或一个数据范围,任何不变量差异、尾延迟越线或恢复时长超标都立即回退;最后把复现步骤、阈值和修复证据写入运行手册。 - 追问:广播查询一定不能用吗?
- 直接回答:不是,但要低频、有界、可降级,并纳入全分片最慢节点成本。
- 追问:分片内命中索引能消除扇出吗?
- 直接回答:不能,只能降低各分片局部成本,网络与协调仍存在。
- 追问:路由元数据陈旧怎么办?
- 直接回答:根据版本冲突刷新元数据,并检查配置服务器与控制面健康。
- 详情:定向与广播路由
- 综合问题:一次 chunk(数据块)迁移经历什么,失败时如何保证路由正确?
- 口述答案:数据块迁移不是复制一个固定文件,而是转移一段分片键范围。balancer(均衡器)先选择源范围,目标分片克隆基线文档并承担索引写入,再追赶迁移期间变化;校验满足后才向 config server(配置服务器)提交新归属与版本,mongos(路由进程)刷新路由,最后源端清理旧范围。提交前若网络、目标磁盘或追赶失败,正确策略是保持旧归属,不让客户端在两个位置猜权威;清理残留、扩容或限速后重试。迁移同时消耗源读、目标写、网络、cache(缓存)、oplog(操作日志)和临时磁盘,可能放大前台尾延迟与复制延迟。排障要记录迁移阶段、元数据版本、源目标文档计数和主机水位两类证据。止血先暂停或限速均衡,保护业务写与至少一个健康副本,绝不能在归属未确认时手工删源。回归从小块灰度,校验路由目标、计数、业务摘要、失败重试和源端清理,并在迁移中注入断网与磁盘高水位,证明失败仍由旧归属提供正确数据。 容量评估还要加入增长和故障余量:用高分位文档、最大租户、峰值写率与最长保留期推算半年后的数据、索引、日志、临时空间和网络,而不是按平均值配置。验证时让一个成员落后或一个分片变慢,观察请求是否正确降级,后台任务是否抢占前台资源。恢复后执行文档计数、关键字段类型分布、业务聚合与抽样明细四层校验,并对差异保留可追溯清单。只有容量水位、数据正确性和故障恢复同时达标,方案才可进入生产。
- 追问:迁移时是否完全没有重复数据?
- 直接回答:克隆阶段物理上可能两端都有副本,权威归属由元数据提交点决定。
- 追问:迁移慢就提高并发吗?
- 直接回答:可能挤压复制和前台写,应先看瓶颈和磁盘、网络余量。
- 追问:何时清理源范围?
- 直接回答:新归属提交并稳定后按受支持流程清理,不能提前手工删除。
- 详情:数据块迁移
- 综合问题:jumbo chunk(超大数据块)和 zone(区域)约束冲突如何治理?
- 口述答案:jumbo chunk(超大数据块)的本质常是分片键范围缺少有效拆分点,例如单一租户或单一键值聚集过多数据,不只是块尺寸大。zone(区域)则把特定键范围限制到指定分片组,用于地域、合规或硬件隔离;若区域容量不足、规则重叠或超大块无法拆分,balancer(均衡器)就算持续运行也无法达到目标。排障先看分片键值分布、块范围和可拆点,再看区域规则、各区域磁盘余量、迁移失败阶段与业务热度。止血是暂停反复失败迁移,保护目标磁盘和前台写,对热点租户限流或隔离查询;不要靠手工切任意边界假装解决同值聚集。长期方案可能是引入高基数后缀、把大租户拆桶、重新分片,或扩充区域内分片容量,同时规划全量搬迁、双读校验和回滚。回归用最大租户数据复制场景,验证块可拆、可迁移、zone(区域)归属正确、路由定向且故障域符合要求。业务结论是区域规则不能创造容量,均衡器也不能修复错误分片键。 我还会明确可观测门槛与操作顺序:告警必须能从业务请求标识关联到数据库操作、成员水位、路由目标和主机资源,值班人员先保护权威事实,再限制非关键流量,最后处理容量与性能。任何临时参数调整都记录原值、适用版本、影响范围和恢复时点,避免止血措施永久化。修复后重放事故时间窗的真实负载,比较错误率、尾延迟、未知结果停留、复制追赶与磁盘余量,并由业务聚合和流水对账共同签收。
- 追问:手工多切数据块能解决同值聚集吗?
- 直接回答:通常不能,同一键值缺少可用拆分边界,需要改变键或模型。
- 追问:zone(区域)只影响存储吗?
- 直接回答:还影响路由、容量、故障切换和迁移可选目标。
- 追问:重新分片前先做什么?
- 直接回答:量化全量与增量、临时空间、校验、切流和回滚窗口,并核对版本能力。
- 详情:超大数据块与区域
- 综合问题:cache(缓存)压力、文档搬移和多索引写放大同时出现时怎样排障?
- 口述答案:先确认业务影响和权威写是否成功,再把现象分成缓存、文档与索引三条证据链。内部证据看 dirty page(脏页)、驱逐、读入写出、检查点与索引尺寸;外部证据看文档大小分位数、更新字段、磁盘尾延迟和请求来源。若大 document(文档)频繁增长,存储引擎可能重写更多内容;每个二级索引还要删除旧键、插入新键,journal(预写日志)、oplog(操作日志)和副本继续放大。此时 cache(缓存)中热点被脏页和扫描争夺,写延迟与点查延迟会一起上升。止血先限异常批写与大扫描,暂停非关键迁移和聚合,保护磁盘水位;不能直接删索引或重启。长期把无界数组拆分,减少高频变化字段上的冗余索引,按查询形状保留必要索引,并让批写有上限和退避。回归使用真实文档尺寸、五个与精简后索引方案对比,记录每次业务更新产生的磁盘写、复制流量、缓存驱逐和尾延迟,再做冷缓存与故障恢复。只有业务吞吐恢复且复制水位、磁盘与数据校验都稳定,才算闭环。 为了防止局部优化制造新风险,我会把读取收益与写放大、缓存占用、副本延迟、迁移成本和恢复时间放在同一张对照表中。测试数据包含高低选择率、长短文档、大租户和迟到重复事件,压测期间同时执行备份、检查点或受控迁移,观察资源竞争。发布前准备向前兼容和回滚脚本,发布后持续校验权威记录与派生结果;若出现路由扇出、成员落后、磁盘逼近门槛或业务差异,立即停止扩量并按既定状态恢复。
- 追问:第一类证据为什么不能只看处理器?
- 直接回答:缓存驱逐和随机输入输出可在处理器不高时造成严重尾延迟。
- 追问:删索引前要确认什么?
- 直接回答:依赖查询、约束职责、分片路由和回滚方案,并先灰度验证。
- 追问:文档搬移是版本稳定事实吗?
- 直接回答:具体实现与度量需现场核对,但文档增长带来的重写和资源成本必须实测。
- 详情:维护排障
- 综合问题:复制延迟、频繁选举、连接池耗尽和磁盘高水位如何联合处置?
- 口述答案:这类事故容易形成因果环:磁盘变慢让 journal(预写日志)、检查点和从节点应用落后,心跳或进程暂停触发选举,客户端重连与重试放大连接池,更多并发又压高磁盘。处置先冻结时间线,记录业务错误、任期、成员水位、连接等待、磁盘延迟和请求标识。止血优先保护多数与权威写:限制非关键查询和重试速率,暂停块迁移与批任务,把关键读回主但设置总量上限,隔离明确不稳定成员;不能同时重启多个副本,也不能无限扩大连接池。随后区分根因是磁盘容量、输入输出尾延迟、网络、长事务还是应用重试风暴,修复后让落后成员逐个追赶。若 oplog(操作日志)窗口不足,按容量计划重新同步。回归在压测环境注入磁盘延迟和单节点断连,验证选举次数、恢复时间、连接峰值、未知结果率、幂等命中和业务差额。监控门槛要覆盖磁盘水位、复制获取/应用延迟、任期变化与连接排队,确保下一次在业务失败前触发降级。 进一步落地时,我会先在影子环境固定数据快照、请求参数、并发和资源配额,记录变更前后的扫描量、写入量、复制水位、磁盘峰值与业务结果;再只改变一个关键因素做对照,避免把缓存升温或流量波动误当收益。故障注入必须覆盖网络断开、进程切换、磁盘变慢和重复请求,恢复后按稳定业务键核对数量、状态、版本与流水。发布采用小流量灰度,设置停止条件和回滚点,观察窗口跨过一个完整高峰;监控、处置手册和责任人齐备后才扩大范围。
- 追问:扩大连接池为何可能更糟?
- 直接回答:会把更多并发压到已过载节点,增加排队、超时和重试风暴。
- 追问:能否同时重启所有从节点追平?
- 直接回答:不能轻率操作,会失去多数余量和可恢复副本,应逐个验证。
- 追问:如何确认事故结束?
- 直接回答:业务错误归零、成员追平、任期稳定、磁盘和连接回落,并完成数据对账。
- 详情:维护事故闭环
- 综合问题:WMS(仓储管理系统)商品扩展属性适合 MongoDB(文档数据库),库存也应一起迁入吗?
- 口述答案:不能因为商品属性适合文档模型,就把库存裁决一并迁入。商品扩展属性形状多样、常随商品整体读取、数量有界,适合嵌入商品 document(文档),通过 schema validation(模式校验)约束属性名、类型和数量。库存则有“不超卖、预占与实存守恒、流水可追溯、重复请求只生效一次”等不变量,还涉及仓库、货主、批次和并发条件。它是否使用 MongoDB(文档数据库)必须独立证明条件更新、唯一幂等键、事务边界、失败恢复和对账能力,不能只依赖单文档原子。稳妥方案可让权威库存与流水保持明确裁决模型,商品文档只冗余可重建展示库存并带版本与更新时间;下单裁决始终回权威路径。若确需同库实现,库存数量、版本和幂等流水应在受控原子边界更新,跨文档和跨系统部分用事件与对账收敛。验证注入并发扣减、响应丢失、主节点切换、重复消息和投影延迟,检查数量不为负、流水唯一、汇总可重算。选型结论必须由不变量证据决定,而不是由字段灵活性决定。 工程验证不能只跑一次成功样例。我会准备正常、边界、热点、陈旧和损坏五类数据,分别测冷启动、热稳态与高峰并发,保存查询计划、成员进度、主机资源和业务日志的同一时间线。发生超时先区分明确失败、已经成功与结果未知,再按幂等键收敛,禁止依赖猜测重放。上线先灰度一个租户或一个数据范围,任何不变量差异、尾延迟越线或恢复时长超标都立即回退;最后把复现步骤、阈值和修复证据写入运行手册。
- 追问:展示库存可以陈旧吗?
- 直接回答:可按业务预算陈旧,但必须标时间,真正下单仍回权威裁决。
- 追问:单文档原子就能防超卖吗?
- 直接回答:还要有条件更新、幂等、状态机和流水对账,跨文档边界另行处理。
- 追问:商品与库存拆开会不会多查一次?
- 直接回答:展示可用可重建摘要减少读取,正确性请求不能拿性能换不变量。
- 详情:项目边界
- 综合问题:跨境物流、Runner(执行器)和 IoT(物联网)三类数据如何分别建模?
- 口述答案:三类数据都“字段多变”,但聚合边界不同。跨境物流主 document(文档)保存运单标识、最新节点、摘要和水位,原始轨迹按事件拆分,使用承运商事件号或业务组合键幂等,按事件时间与接收序号处理迟到。Runner(执行器)配置适合不可变版本文档,校验完整后原子切换激活指针,执行节点读取指定版本并回报应用水位,避免 5000 条配置长事务。IoT(物联网)设备元数据可按设备聚合,嵌入有界能力与标签,通过 schema validation(模式校验)限制关键类型;高频遥测和报警事件不能无限嵌入,应进入独立时序或事件模型。三者共享的治理是字段版本、大小上限、幂等、保留期和重建路径,但不能用同一个万能集合。验证分别回放轨迹乱序重复、配置切换故障和设备字段异型,观察文档大小、索引项、查询路由与恢复结果。失败边界是轨迹数组超限、配置半发布和设备动态字段爆炸。项目话术应说明为什么聚合、哪部分拆分、谁是权威源,以及故障后怎样校验。 容量评估还要加入增长和故障余量:用高分位文档、最大租户、峰值写率与最长保留期推算半年后的数据、索引、日志、临时空间和网络,而不是按平均值配置。验证时让一个成员落后或一个分片变慢,观察请求是否正确降级,后台任务是否抢占前台资源。恢复后执行文档计数、关键字段类型分布、业务聚合与抽样明细四层校验,并对差异保留可追溯清单。只有容量水位、数据正确性和故障恢复同时达标,方案才可进入生产。
- 追问:轨迹摘要如何防止晚到事件覆盖?
- 直接回答:比较业务事件时间、序号和版本,按规则幂等更新并可从事件重建。
- 追问:配置为何采用不可变版本?
- 直接回答:便于完整校验、原子激活、回滚和审计,缩小事务边界。
- 追问:设备遥测为何不嵌入元数据?
- 直接回答:遥测高频无界,会制造大文档、热点和多键索引放大。
- 详情:项目映射
- 综合问题:为什么多数确认不等于库存和资金业务完成?
- 口述答案:多数确认只证明 MongoDB(文档数据库)写入达到配置要求的副本承诺,它不知道消息是否投递、下游是否消费、支付渠道是否扣款、库存投影是否更新,也不知道对账是否一致。一次支付可能先在数据库记录“处理中”,随后调用渠道;数据库多数写成功而渠道响应丢失,结果仍未知。反过来,渠道成功而本地状态更新失败,也不能靠数据库事务回滚外部动作。正确模型是稳定业务幂等键、明确状态机、权威流水、可重放事件、渠道查单、补偿和定期对账。库存也要区分权威扣减与商品文档展示摘要,摘要落后不能触发重复扣减。故障处理中先冻结同一业务键的新动作,从 Primary(主节点)查询权威记录,再查渠道或库存流水,按证据收敛到成功、失败或人工处理。验证注入提交后丢响应、消息重复、消费者停机、渠道超时和副本延迟,证明金额守恒、库存不负、流水唯一且未知状态最终归零。多数确认是在延迟、耐久和可用性之间的数据库取舍,端到端完成必须由业务协议证明。 我还会明确可观测门槛与操作顺序:告警必须能从业务请求标识关联到数据库操作、成员水位、路由目标和主机资源,值班人员先保护权威事实,再限制非关键流量,最后处理容量与性能。任何临时参数调整都记录原值、适用版本、影响范围和恢复时点,避免止血措施永久化。修复后重放事故时间窗的真实负载,比较错误率、尾延迟、未知结果停留、复制追赶与磁盘余量,并由业务聚合和流水对账共同签收。
- 追问:多文档事务能覆盖支付渠道吗?
- 直接回答:不能,外部系统不参与同一数据库事务,需要查单、幂等和补偿。
- 追问:消息发送成功就完成了吗?
- 直接回答:还要证明消费幂等、业务落账和最终对账,发送确认只是中间边界。
- 追问:为何不能从从节点判定未扣款?
- 直接回答:从节点可能陈旧,查空会把复制延迟误判为未发生。
- 详情:确认与业务完成边界
- 综合问题:项目版本待现场核对时,如何给出可靠的 MongoDB(文档数据库)设计与排障结论?
- 口述答案:我会把稳定机制、版本敏感行为和现场事实分三层表达。稳定机制可以讲文档聚合、日志与检查点分工、写读关注、副本选举、查询路由和数据块迁移;项目版本、驱动、部署形态、默认关注级别、可重试读写、事务限制、聚合内存与磁盘溢写、
TTL(生存时间)调度、均衡器、重新分片和备份格式统一写“待现场核对”。核对卡至少记录准确版本、配置来源、官方章节、最小复现实验、升级兼容与回退边界。比如不能断言聚合默认允许溢写,而要在目标环境构造有界数据,观察计划、临时磁盘和失败行为;不能断言某种重试自动开启,而要检查驱动连接配置并注入提交后断连。设计结论也必须绑定量级:文档大小分位数、数组长度、查询定向率、复制窗口、磁盘水位和恢复时间。若现场证据与学习基线不同,以现场行为为准并更新核对卡。最终通过单文件审计、全部图真实渲染、故障演练和业务对账形成闭环,既避免“最新版”断言,也不因版本未知而停留在空泛概念。 为了防止局部优化制造新风险,我会把读取收益与写放大、缓存占用、副本延迟、迁移成本和恢复时间放在同一张对照表中。测试数据包含高低选择率、长短文档、大租户和迟到重复事件,压测期间同时执行备份、检查点或受控迁移,观察资源竞争。发布前准备向前兼容和回滚脚本,发布后持续校验权威记录与派生结果;若出现路由扇出、成员落后、磁盘逼近门槛或业务差异,立即停止扩量并按既定状态恢复。 - 追问:版本未知是否什么都不能说?
- 直接回答:可以讲稳定机制和核对方法,但默认值、限制和支持范围不能猜。
- 追问:官方文档是否足够?
- 直接回答:还要核对现场配置、驱动和最小实验,因为部署可能覆盖默认行为。
- 追问:何时更新核对卡?
- 直接回答:版本、驱动、拓扑、关键配置或恢复方案变化时都应更新并回归。
- 详情:版本核对卡
7. 复习与验收清单
- 能从 BSON(二进制 JSON 文档)讲到 collection(集合)、document(文档)、16
MiB(兆字节)边界、数组增长与模式治理。 - 能完整复述 WiredTiger(存储引擎)cache(缓存)、page(页)、journal(预写日志)、checkpoint(检查点)和 oplog(操作日志)链路。
- 能区分 write concern(写关注)、read concern(读关注)、read preference(读偏好)、多数提交点、会话与跨系统完成。
- 能用查询形状、ESR(相等排序范围)、多键索引、覆盖、分页、聚合溢写和 explain(执行计划)解释退化。
- 能演绎选举、追赶、回滚、陈旧读、定向/广播、热点、数据块迁移、超大数据块和区域约束。
- 能用两类证据完成缓存、写放大、慢聚合、复制、选举、迁移、字段、连接池和磁盘事故闭环。
- 能把 WMS(仓储管理系统)、跨境物流、Runner(执行器)和 IoT(物联网)项目绑定到权威源、幂等、事务与对账。
- 已将项目版本和学习基线均登记为“待现场核对”,版本敏感行为不作默认值或“最新版”断言。
