--- id: backend.mongodb.interview title: MongoDB 面试题 type: interview domain: backend stack: [mongodb] tags: [MongoDB, 文档模型, 索引, 事务, 面试] level: basic related: [backend.mongodb.document-model] week: [14, 16] --- # MongoDB 面试题 ## 基础 ### 【基础】MongoDB 和 MySQL / PostgreSQL 怎么选? **参考答案**:结构多变、以文档为单位读写、写入量大、可以接受反范式的数据(日志、对话、内容、配置)适合 MongoDB;强一致事务、复杂关联查询和报表(订单、账务)更适合关系型。很多系统组合使用。 **追问**:PostgreSQL 的 JSONB 能不能代替?→ 中小规模、需要和关系数据一起做事务时完全可以;数据量大、结构差异大、需要水平分片时 MongoDB 更自然。 ### 【基础】嵌入和引用怎么选? **参考答案**:一起读写、数量有上限、不被单独访问的子数据嵌入;数量无上限、被多处共享或独立访问的数据引用。要考虑 16MB 单文档上限。 **追问**:订单里的商品信息嵌入还是引用?→ 嵌入下单时的快照(价格、名称),同时保留商品 id 引用,因为订单要记录成交时的状态。 ### 【基础】MongoDB 支持事务吗? **参考答案**:单文档操作天然原子;4.0 起支持副本集上的多文档事务,4.2 起支持分片集群。事务有性能开销和时间限制,优先通过建模减少对多文档事务的依赖。 **追问**:单机能用事务吗?→ 不能,需要副本集,开发环境可以启动单节点副本集。 ## 进阶 ### 【进阶】复合索引怎么设计? **参考答案**:按 ESR 规则:等值字段在前、排序字段中间、范围字段在后;考虑选择性和查询覆盖;用 `explain` 验证是否走索引、是否有内存排序。 **追问**:为什么范围字段放在排序字段之前会有问题?→ 范围扫描后的结果在排序字段上不再有序,需要额外的内存排序。 ### 【进阶】深分页怎么优化? **参考答案**:避免大 `skip`(需要扫描并丢弃前面的文档),改用基于游标的分页:记录上一页最后一条的排序字段值(加 `_id` 打破平局),下一页查询“小于这个值”的数据。 **追问**:这种分页的缺点?→ 不能直接跳到第 N 页,适合无限滚动和“下一页”场景。 ### 【进阶】变更流有什么用?怎么保证不丢事件? **参考答案**:订阅数据变更,用于同步到搜索引擎、缓存、消息队列、审计。消费者处理完后保存 resume token,重启时从 token 继续;消费端做幂等;注意 oplog 保留窗口,停太久 token 会失效,需要全量重建。 **追问**:和 PostgreSQL 的什么机制类似?→ 逻辑复制 / CDC(如 Debezium 读取 WAL),以及 outbox 模式。 ## 场景题 ### 【场景题】AI 客服的对话记录怎么存储? **参考答案**:会话文档嵌入最近消息,完整消息单独集合并以 `conversation_id + seq` 唯一索引保证顺序和幂等;带 `tenant_id` 做隔离;按 ESR 建列表索引;变更流同步 ES 做检索;按保留期归档。详见[会话存储场景](scenarios/conversation-store.md)。 **追问**:如何防止越权查询?→ 数据访问层统一注入 `tenant_id` 条件,并有测试覆盖“缺少租户条件时拒绝执行”。 ### 【场景题】线上一个查询突然变慢,你怎么排查? **参考答案**:开启慢查询分析(profiler 或日志),对慢查询执行 `explain("executionStats")` 看扫描的文档数与返回数之比、是否 COLLSCAN 或内存排序;检查是否新增了查询条件导致索引不匹配、数据分布变化、索引过多影响写入;修复后对比执行统计。 **追问**:建索引会影响线上吗?→ 会占用资源,选择低峰期建立,大集合关注构建进度和副本同步延迟。