<?xml version="1.0" encoding="utf-8"?>
<ul>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本速查以 MongoDB 7.x / 8.x 通用用法为主。不同版本命令、参数和默认值可能变化，生产操作前以当前版本文档为准。核心概念            概念      说明                  Database      数据库              Collection      集合              Document      BSON 文档              Field      字段              _id      文档唯一标识              Replica Set      副本集              Primary      接收写入              Secondary      同步副本              Oplog      复制日志              Shard      数据分片              mongos      分片路由              Config Server      分片元数据              WiredTiger      默认存储引擎      常用端口            组件      默认端口                  mongod / mongos      27017      生产环境只监听内网地址，不应暴露公网。连接单节点：mongodb://user:password@mongo1:27017/shop?authSource=admin副本集：mongodb://user:password@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&amp;authSource=admin分片集群：mongodb://user:password@mongos1:27017,mongos2:27017/shop?authSource=adminDocker 快速启动docker run -d \  --name mongo \  -p 27017:27017 \  -e MONGO_INITDB_ROOT_USERNAME=admin \  -e MONGO_INITDB_ROOT_PASSWORD=secret123 \  -v mongo-data:/data/db \  mongo:7.0进入 shell：docker exec -it mongo mongosh -u admin -p secret123数据库与集合show dbsuse shopshow collections创建集合：db.createCollection("orders")删除集合：db.orders.drop()查看统计：db.orders.stats()插入db.orders.insertOne({  _id: "o_1",  user_id: "u_1",  status: "CREATED",  amount: NumberDecimal("100.00")})db.orders.insertMany([  { _id: "o_2", user_id: "u_1", status: "CREATED" },  { _id: "o_3", user_id: "u_2", status: "CREATED" }], { ordered: false })查询db.orders.findOne({ _id: "o_1" })db.orders.find({  user_id: "u_1",  status: { $in: ["CREATED", "PAID"] }})db.orders.find(  { user_id: "u_1" },  { status: 1, amount: 1, created_at: 1 }).sort({ created_at: -1 }).limit(20)更新db.orders.updateOne(  { _id: "o_1", status: "WAIT_PAY" },  {    $set: { status: "PAID", paid_at: new Date() },    $inc: { version: 1 }  })常用操作符：            操作符      说明                  $set      设置字段              $unset      删除字段              $inc      增减              $push      数组追加              $pull      数组删除              $addToSet      去重追加              $currentDate      更新时间      删除db.orders.deleteOne({ _id: "o_1" })db.orders.deleteMany({ status: "CLOSED", created_at: { $lt: cutoff } })索引db.orders.createIndex({ user_id: 1, created_at: -1 })db.orders.createIndex(  { order_no: 1 },  { unique: true })db.sessions.createIndex(  { expires_at: 1 },  { expireAfterSeconds: 0 })db.orders.getIndexes()db.orders.dropIndex("user_id_1_created_at_-1")执行计划db.orders.find({ user_id: "u_1" }).explain("executionStats")重点：winningPlanIXSCAN / COLLSCANkeysExamineddocsExaminednReturnedSORT聚合db.orders.aggregate([  { $match: { status: "PAID" } },  { $unwind: "$items" },  {    $group: {      _id: "$items.sku_id",      quantity: { $sum: "$items.quantity" },      amount: { $sum: "$items.price" }    }  },  { $sort: { quantity: -1 } },  { $limit: 20 }])事务const session = db.getMongo().startSession();try {  session.startTransaction({    readConcern: { level: "snapshot" },    writeConcern: { w: "majority" }  });  session.getDatabase("shop").orders.updateOne(    { _id: "o_1", status: "WAIT_PAY" },    { $set: { status: "PAID" } }  );  session.commitTransaction();} catch (e) {  session.abortTransaction();} finally {  session.endSession();}副本集rs.status()rs.conf()db.hello()rs.printSecondaryReplicationInfo()db.printReplicationInfo()主动切换：rs.stepDown(120)分片sh.status()sh.enableSharding("shop")sh.shardCollection("shop.orders", { tenant_id: 1, user_id: 1 })sh.isBalancerRunning()sh.stopBalancer()sh.startBalancer()Change Streamconst stream = db.orders.watch([], {  fullDocument: "updateLookup"});const event = stream.next();db.sync_tokens.updateOne(  { name: "orders" },  { $set: { token: event._id, updated_at: new Date() } },  { upsert: true })读写关注db.orders.insertOne(  { _id: "o_1", status: "CREATED" },  { writeConcern: { w: "majority", j: true, wtimeout: 3000 } })db.orders.find({ _id: "o_1" }).readPref("primary")监控db.serverStatus()db.currentOp({ active: true, secs_running: { $gte: 60 } })关键指标：connections_currentopcountersquery_latencycache_bytescache_dirty_bytespages_read_into_cachereplication_lag_secondsoplog_windowdisk_usagetransactions_write_conflicts备份恢复mongodump --uri="mongodb://backup:pass@mongo1,mongo2,mongo3/shop?replicaSet=rs0" --gzip --out=/backupmongorestore --uri="mongodb://admin:pass@target:27017/admin" --gzip --drop /backup大规模生产环境优先使用快照或平台备份，并定期恢复演练。安全检查  启用访问控制；  禁止公网暴露；  按服务创建最小权限用户；  启用 TLS；  敏感字段脱敏；  审计高危操作；  密钥进入 KMS 或 Secret；  定期复核账号权限。常见错误            错误      排查                  Connection refused      进程、端口、bindIp              Authentication failed      密码或 authSource              Not primary      写到 Secondary 或选举中              Duplicate key      唯一索引冲突              Query exceeded memory      排序或聚合无索引              WriteConflict      并发更新同一文档              Not primary and secondary ok      拓扑状态或读偏好      生产上线清单  副本集至少三数据节点；  跨故障域部署；  认证和 TLS 开启；  业务用户最小权限；  高频查询有索引；  文档和数组大小可控；  一致性矩阵明确；  备份和恢复演练完成；  监控告警接入；  容量模型和扩容预案完成；  分片键经过评审；  升级回滚方案明确。思考题  你的核心集合使用什么读写关注组合？  最近一次慢查询 Top 10 是否都验证过 explain？  副本集 oplog 窗口能覆盖多长故障？  备份能否恢复用户、索引和分片元数据？  工作集是否已经接近内存上限？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。从会用 MongoDB 到驾驭 MongoDB，需要经历查询优化、副本集治理、分片架构、数据模型治理和源码级理解。最终能力体现在能结合业务约束做出可靠架构决策。31.1 学习路线            阶段      重点                  入门      CRUD、mongosh、文档模型              进阶      索引、聚合、explain、Schema              高级      副本集、事务、Change Stream、备份              专家      分片、容量、性能、故障演练              大师      架构选型、平台治理、源码贡献      31.2 源码阅读推荐路径：driver operation  -&gt; server command  -&gt; query planner  -&gt; storage API  -&gt; WiredTiger  -&gt; replication oplog  -&gt; election  -&gt; sharding metadata重点模块：  查询优化器；  执行器；  索引扫描；  WiredTiger 接口；  oplog 应用；  副本集状态机；  选举协议；  chunk split / migrate；  mongos 路由；  事务协调。从跑通单测和跟踪一条 update 开始。31.3 实验环境必做实验：  三节点副本集；  Primary 切换；  oplog 调整；  Secondary 读；  多文档事务；  Change Stream；  三 shard 集群；  哈希与范围分片对比；  chunk 迁移；  备份恢复；  慢查询优化；  cache 压力模拟。记录版本、配置、数据量、指标和结论。31.4 平台化治理MongoDB 平台应提供：  集群和库生命周期管理；  权限申请与审计；  Schema 契约和 validator；  索引评审；  慢查询看板；  容量预测；  备份恢复演练；  变更审批；  成本分析；  Runbook。31.5 架构能力继续学习：  文档建模与 DDD 聚合；  分布式一致性；  Raft 与共识；  存储引擎；  B+Tree 与 cache；  分布式事务；  CDC 与数据平台；  多租户隔离；  数据安全；  混沌工程。架构方案必须回答一致性、容量、故障、观测、成本和回滚。31.6 版本跟踪关注：  新版本默认行为变化；  查询优化器改进；  分片能力；  时间序列集合；  搜索能力；  安全增强；  性能优化；  企业审计；  云托管特性；  驱动兼容。以官方版本文档为准，不直接套用旧博客结论。31.7 个人知识库沉淀：  Schema 评审模板；  索引设计清单；  慢查询案例；  分片压测报告；  故障时间线；  升级方案；  备份演练记录；  一致性矩阵；  常用命令；  源码笔记。本章小结大师之路建立在真实数据和故障经验上。通过源码、实验、平台化和架构治理，把 MongoDB 从一个存储产品变成可控、可观测、可演进的基础设施。思考题  你当前集群的一致性矩阵是否明确？  最需要补齐的监控是什么？  最近一次备份演练是否验证路由和用户？  哪些集合存在无界增长风险？  分片键是否有容量模型支撑？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章整理 MongoDB 高频面试题。回答时先给结论，再讲机制，最后补充生产实践、性能边界和故障处理。30.1 MongoDB 是什么数据库？MongoDB 是面向文档的分布式数据库，使用 BSON 文档保存数据，集合组织文档，支持丰富索引、聚合管道、副本集、分片和事务。加分回答：  单文档操作原子；  多文档事务可用但应短小；  副本集提供高可用；  分片提供水平扩展；  Schema 灵活但仍需治理。30.2 MongoDB 支持 ACID 吗？支持：  单文档更新天然原子；  副本集和分片集群支持多文档 ACID 事务；  事务需要 session、副本集环境；  分片事务成本更高；  应优先设计单文档边界。不要回答“不支持事务”，这个结论已经过时。30.3 内嵌和引用如何选择？内嵌适合：  一起读取；  数量有限；  更新频率低；  生命周期一致。引用适合：  无界增长；  独立查询；  多方修改；  文档过大。核心是按访问模式建模，而不是机械翻译关系表。30.4 为什么要避免无界数组？风险：  文档持续增长；  写放大；  复制延迟增加；  索引开销增加；  查询投影浪费；  接近文档大小上限。订单流水、评论、日志应拆集合并分页。30.5 复合索引如何设计？遵循 ESR：Equality -&gt; Sort -&gt; Range示例：db.orders.createIndex({  tenant_id: 1,  status: 1,  created_at: -1})同时满足最左前缀，并让排序字段进入索引，避免内存排序。30.6 explain 怎么看？重点字段：            字段      判断                  winningPlan.stage      COLLSCAN 或 IXSCAN              keysExamined      索引扫描量              docsExamined      文档扫描量              nReturned      返回量              SORT      内存排序              executionTimeMillis      总耗时      理想情况是 nReturned 接近 keysExamined。30.7 副本集如何工作？流程：client write -&gt; primary -&gt; oplog -&gt; secondary applyPrimary 故障后，多数投票成员选主。生产至少三个数据节点，跨故障域部署，关键写使用 majority。30.8 什么是 oplog？oplog 是 local 库中的固定大小集合，记录可幂等重放的变更，用于 Secondary 同步和增量恢复。关注：  oplog 窗口；  写入速率；  复制延迟；  Secondary 是否可增量追赶；  初始同步时间。30.9 读写关注如何组合？            场景      组合                  支付后读      majority 写 + primary 读              报表      majority 写 + secondaryPreferred 读              用户画像      可接受延迟读              强一致读      linearizable 或 primary + majority      写关注决定确认范围，读关注决定数据级别，读偏好决定成员。30.10 为什么会回滚？Primary 写入后未复制到多数节点就发生故障，新 Primary 当选后，旧 Primary 恢复时会回滚这些未提交写入。防范：  majority 写关注；  监控复制延迟；  合理多数派拓扑；  处理 rollback 文件；  演练切主。30.11 分片键如何选择？目标：  高基数；  低频率；  写入均匀；  高频查询可定向；  单调递增热点可控；  事务跨 shard 少。常见选择是 tenant_id + user_id、device_id + timestamp、conversation_id + message_id。30.12 哈希分片和范围分片怎么选？            类型      优势      代价                  哈希      写入均匀      范围查询常广播              范围      范围查询高效      递增键易热点      订单按用户查多于按时间范围扫全表时，可以考虑用户哈希或复合键。30.13 什么时候分片？分片信号：  单机磁盘不足；  写入超过单机能力；  工作集长期超过内存；  数据增长明确；  索引和备份时间过长。不解决：  慢查询无索引；  单热点文档；  应用连接滥用；  磁盘临时故障。30.14 Change Stream 的原理是什么？基于 oplog 的可恢复事件流，支持集合、库和集群级监听，可通过 resume token 恢复。生产要点：  token 持久化；  事件幂等；  全量 + 增量；  oplog 窗口足够；  消费积压监控。30.15 WiredTiger cache 为什么重要？常用数据和索引页保存在 cache。工作集超过 cache 会增加磁盘读、延迟波动和 evict 压力。优化：  控制工作集；  减少无效索引；  投影；  冷热分离；  扩内存或分片。30.16 MongoDB 为什么会出现慢查询？常见原因：  COLLSCAN；  索引选择性差；  无索引排序；  大文档返回；  聚合数据放大；  cache 压力；  磁盘瓶颈；  写冲突。排查路径是 Profiler -&gt; explain -&gt; 索引 -&gt; 资源指标。30.17 MongoDB 和 MySQL 如何选？MongoDB 适合文档对象、灵活字段、水平分片、Change Stream 和 JSON 契约。MySQL 适合强关系、复杂事务、强约束和成熟 SQL 生态。不要绝对化，可通过混合架构和 CDC 组合。30.18 生产必须做哪些保障？  副本集跨故障域；  启用认证和 TLS；  最小权限；  监控复制和 cache；  慢查询治理；  索引评审；  备份恢复演练；  容量规划；  升级回滚方案；  故障演练。本章小结MongoDB 面试重点集中在文档建模、索引执行计划、副本集选举、oplog、一致性、分片键、事务、Change Stream 和 WiredTiger。展示生产经验的关键是说明监控、压测、备份和故障演练。思考题  为什么单文档原子性是设计优势？  如何用 ESR 设计索引？  majority 写是否等于不丢任何数据？  分片键选择错误如何补救？  Change Stream 与 oplog 的关系是什么？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章设计一个“商品与订单中心”的 MongoDB 方案，覆盖 Schema、索引、CRUD、状态机、聚合、Change Stream、分片和监控。示例以学习为主，生产地址和密钥需按环境替换。29.1 业务需求核心用例：  按分类和关键词查询商品；  查看商品详情；  用户查看订单列表；  订单支付状态流转；  统计商品销量；  同步商品到搜索系统；  未来支持租户扩展。数据特点：  商品读取多、更新中频；  订单按用户增长；  商品详情较大；  订单明细有限；  搜索同步要求增量。29.2 集合设计商品主表：{  _id: "sku_10001",  tenant_id: "t_100",  title: "轻薄笔记本",  category_id: "c_notebook",  price: NumberDecimal("5999.00"),  status: "ON_SALE",  summary: "16 核 32GB",  keywords: ["轻薄", "笔记本"],  attrs: {    cpu: "16核",    memory: "32GB"  },  sales_count: 1200,  version: 3,  created_at: new Date(),  updated_at: new Date()}商品详情表：{  _id: "sku_10001",  content: "&lt;html&gt;...&lt;/html&gt;",  images: ["a.jpg", "b.jpg"]}订单表：{  _id: "o_202608250001",  tenant_id: "t_100",  user_id: "u_1001",  status: "WAIT_PAY",  total_amount: NumberDecimal("5999.00"),  item_count: 1,  items: [    {      sku_id: "sku_10001",      title: "轻薄笔记本",      price: NumberDecimal("5999.00"),      quantity: 1    }  ],  shipping_address: {    city: "Shanghai",    street: "南京西路 100 号"  },  version: 1,  created_at: new Date(),  updated_at: new Date()}29.3 索引设计商品：db.products.createIndex({ tenant_id: 1, category_id: 1, status: 1, sales_count: -1 })db.products.createIndex({ tenant_id: 1, keywords: 1 })db.products.createIndex({ tenant_id: 1, title: 1 })订单：db.orders.createIndex({ tenant_id: 1, user_id: 1, created_at: -1, _id: -1 })db.orders.createIndex({ tenant_id: 1, status: 1, created_at: -1 })设计说明：  查询都带租户前缀；  列表排序进入索引；  _id 保证分页边界唯一；  避免无关字段进入索引；  写入热点可后期评估分片。29.4 创建订单function createOrder(order) {  const result = db.orders.insertOne({    _id: order.orderId,    tenant_id: order.tenantId,    user_id: order.userId,    status: "CREATED",    total_amount: order.totalAmount,    item_count: order.items.length,    items: order.items,    shipping_address: order.address,    version: 1,    created_at: new Date(),    updated_at: new Date()  }, {    writeConcern: { w: "majority", j: true, wtimeout: 3000 }  });  return result;}订单号唯一，重复提交由 _id 唯一约束拦截。29.5 支付状态机function markPaid(orderId, version) {  const result = db.orders.updateOne(    {      _id: orderId,      status: "WAIT_PAY",      version: version    },    {      $set: {        status: "PAID",        paid_at: new Date()      },      $inc: { version: 1 },      $currentDate: { updated_at: true }    }  );  if (result.matchedCount === 0) {    throw new Error("order state changed");  }  return result;}状态机条件防止重复回调、旧请求和并发覆盖。29.6 用户订单分页第一页：db.orders.find(  {    tenant_id: "t_100",    user_id: "u_1001"  },  {    status: 1,    total_amount: 1,    item_count: 1,    created_at: 1  }).sort({ created_at: -1, _id: -1 }).limit(20)下一页：db.orders.find({  tenant_id: "t_100",  user_id: "u_1001",  $or: [    { created_at: { $lt: lastCreatedAt } },    { created_at: lastCreatedAt, _id: { $lt: lastId } }  ]}).sort({ created_at: -1, _id: -1 }).limit(20)29.7 商品销量统计db.orders.aggregate([  {    $match: {      tenant_id: "t_100",      status: "PAID",      created_at: {        $gte: new Date("2026-08-01T00:00:00Z"),        $lt: new Date("2026-09-01T00:00:00Z")      }    }  },  { $unwind: "$items" },  {    $group: {      _id: "$items.sku_id",      quantity: { $sum: "$items.quantity" },      amount: { $sum: "$items.price" },      order_count: { $sum: 1 }    }  },  { $sort: { quantity: -1 } },  { $limit: 50 }])重统计建议移到 Hidden Secondary 或分析库。29.8 搜索同步全量初始化：db.products.find({  tenant_id: "t_100",  status: "ON_SALE"}, {  title: 1,  keywords: 1,  category_id: 1,  price: 1})增量监听：const stream = db.products.watch(  [    {      $match: {        operationType: { $in: ["insert", "update", "replace", "delete"] }      }    }  ],  {    fullDocument: "updateLookup"  })同步程序保存 resume token，并对下游搜索文档执行幂等 upsert。29.9 分片方案当订单规模超过单 shard 容量：sh.enableSharding("shop")sh.shardCollection(  "shop.orders",  { tenant_id: 1, user_id: 1 })理由：  高频查询带 tenant_id 和 user_id；  租户内部用户基数高；  写入按租户和用户分布；  每个租户事务多落在同一 shard；  大租户需要单独监控。29.10 监控与验收指标：product_query_latency_p99order_create_latency_p99order_state_conflict_totalorder_page_query_p99search_sync_lag_secondschange_stream_resume_errorsdisk_usagecache_hit_ratioreplication_lag验收用例：  重复订单号不会重复创建；  重复支付回调只生效一次；  订单分页不重不漏；  商品列表查询走索引；  详情读取不影响列表延迟；  搜索同步可恢复；  分页查询带租户前缀；  副本切换后应用可恢复。本章小结项目实践把文档边界、索引、状态机、游标分页、聚合、Change Stream 和分片键串联起来。商品与订单中心的关键是列表轻量化、详情拆分、状态条件更新和搜索增量同步。思考题  为什么商品详情要拆集合？  订单状态机为什么需要版本？  分页为什么要 created_at + _id？  搜索同步如何处理 token 过期？  订单分片键为什么选择租户和用户？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 和关系型数据库不是简单替代关系。核心差异在数据模型、事务边界、Schema 演进、扩展方式和查询生态。选型应从访问模式和团队能力出发，而不是“哪个更新”。28.1 数据模型关系模型：usersordersorder_itemsproducts文档模型：{  _id: "o_10001",  user_id: "u_1001",  status: "PAID",  total_amount: NumberDecimal("599.00"),  items: [    { sku_id: "sku_10001", quantity: 1 }  ],  shipping_address: { city: "Shanghai" }}            维度      MongoDB      关系型数据库                  结构      文档和集合      表和行              关联      内嵌或 $lookup      JOIN 和外键              Schema      灵活 + validator      强 Schema              类型      BSON      SQL 标准类型              对象映射      贴近对象      ORM 转换      28.2 事务能力MongoDB：  单文档操作天然原子；  多文档事务支持 ACID；  事务应短小；  分片事务成本更高。关系型数据库：  多表事务成熟；  隔离级别体系清晰；  复杂约束和外键能力强；  强事务场景通常更稳。支付、账务、库存等复杂强一致模型，不应仅因为“文档方便”就忽略关系型数据库的成熟约束。28.3 查询能力            能力      MongoDB      关系型数据库                  SQL      NoSQL API      SQL 生态成熟              聚合      聚合管道      GROUP BY / 窗口函数              JOIN      $lookup      JOIN              报表      中小规模      复杂报表生态广              全文      文本索引      数据库实现各异              地理      原生索引      因实现而异              分析      可用但需隔离      数仓生态更常用      MongoDB 管道表达能力强，但复杂分析通常应迁移到 ClickHouse、Snowflake 或其他分析系统。28.4 Schema 演进MongoDB 添加字段：db.products.updateMany(  {},  { $set: { schema_version: 2 } })关系型数据库：ALTER TABLE products ADD COLUMN schema_version INT;差异：            维度      MongoDB      关系型                  新增字段      应用先兼容，可选回填      DDL 与默认值              删除字段      代码停止读写后清理      ALTER TABLE              类型约束      validator + 应用      数据库强约束              回滚      新旧格式兼容      DDL 回滚成本高      灵活 Schema 不代表不需要契约，只是把治理从表结构移到应用和 validator。28.5 扩展方式MongoDB：  副本集提供高可用和读扩展；  分片提供水平扩展；  分片键决定路由和分布；  原生分片运维体系成熟。关系型数据库：  主从或高可用集群；  读写分离；  按业务拆库拆表；  分布式数据库或中间件；  云托管能力差异大。如果数据天然按租户、用户、设备分片，MongoDB 的文档边界和分片模型比较自然。28.6 性能模型MongoDB 优势场景：  按主键取大 JSON 对象；  字段结构变化快；  聚合管道中小型实时统计；  多地域或租户分片；  前置事件或行为存储。关系型优势场景：  复杂多表事务；  强外键和约束；  复杂 SQL 报表；  稳定关系模型；  精细 SQL 优化器生态。性能结论必须基于自己的索引、数据量、并发和磁盘压测。28.7 选型建议优先考虑 MongoDB：  聚合读整个业务对象；  字段结构变化频繁；  多语言 JSON 契约；  租户或设备数据可自然分片；  需要原生 Change Stream；  团队有文档建模经验。优先考虑关系型数据库：  强事务和多表约束；  财务账务系统；  复杂 SQL 和报表；  关系稳定且一致性要求高；  团队 SQL 运维成熟。28.8 混合架构常见组合：orders        MySQL / PostgreSQLproduct doc   MongoDBsearch        Elasticsearchanalytics     ClickHousecache         Redis数据流：business DB -&gt; CDC / Change Stream -&gt; document store / search / analytics混合架构必须统一：  实体 ID；  数据契约；  对账任务；  数据归属；  生命周期。本章小结MongoDB 适合以文档为访问边界的灵活数据模型，关系型数据库适合强关系和强事务模型。两者可以通过 CDC 或 Change Stream 组合。选型的关键问题是：数据如何被读写、事务边界在哪里、未来如何扩展。思考题  哪类业务适合内嵌文档模型？  为什么财务系统常选择关系型数据库？  MongoDB 的灵活 Schema 需要哪些治理？  什么分片场景适合 MongoDB？  混合架构中如何保证数据一致性？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。升级改变数据库版本，迁移改变集群、云环境或数据模型。两者都属于高风险变更，必须有兼容性评估、灰度窗口、验证清单和回滚路径。27.1 升级流程read release notes  -&gt; verify compatibility  -&gt; backup metadata and data  -&gt; upgrade test environment  -&gt; run application tests  -&gt; upgrade secondaries  -&gt; step down primary  -&gt; upgrade primary  -&gt; verify cluster生产升级前必须确认：  当前版本到目标版本的升级路径；  Feature Compatibility Version；  驱动版本兼容；  索引和配置兼容；  备份可恢复；  回滚窗口；  变更审批。27.2 Feature Compatibility Version查看：db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })设置：db.adminCommand({ setFeatureCompatibilityVersion: "7.0" })注意：  只能按官方支持路径操作；  设置后某些旧版本回滚会受限；  必须在集群健康时执行；  命令会传播到副本集；  分片集群还需关注 Config Server 和 shard。27.3 副本集滚动升级推荐顺序：  备份；  先升级 Secondary；  每个节点验证复制和查询；  执行 rs.stepDown()；  升级原 Primary；  恢复后检查状态；  观察应用指标。检查：rs.status()db.serverBuildInfo()db.hello()27.4 分片集群升级一般顺序：  升级 Config Server 副本集；  升级 shard 副本集；  升级 mongos；  检查路由；  暂停或避开 balancer 窗口；  更新驱动配置；  验证分布式查询和事务。分片组件版本兼容性要求更严格，必须按目标版本文档执行。27.5 索引升级大索引上线：  预估空间；  低峰执行；  观察复制延迟；  分批构建；  验证执行计划；  应用查询灰度；  保留删除旧索引脚本。可先在 Hidden Secondary 或预生产环境验证，再推广生产。27.6 数据模型迁移新旧文档共存时：version field  -&gt; dual read compatible  -&gt; write new format  -&gt; backfill old documents  -&gt; verify  -&gt; remove old logic示例：{  schema_version: 2,  shipping_address: {    country: "CN",    city: "Shanghai"  }}应用读取时兼容缺省字段，写入时写新版本，后台任务分批回填。27.7 集群间迁移流程：inventory  -&gt; create target  -&gt; full copy  -&gt; enable change stream or replication  -&gt; catch up  -&gt; verify counts and samples  -&gt; read only switch  -&gt; write switch  -&gt; observe  -&gt; retire source验证：  集合数量；  文档数量；  索引定义；  用户角色；  validator；  分片键；  业务抽样；  应用读写；  监控曲线。27.8 云迁移关注：  VPC 和 DNS；  连接串切换；  TLS 和密钥；  带宽和耗时；  云盘性能等级；  备份权限；  监控接入；  成本模型；  安全审计；  回滚窗口。跨云迁移建议先双读验证，再短暂停写切换，最后处理增量。27.9 回滚策略回滚前提：  旧版本或旧集群仍可运行；  数据没有不可逆 schema 变更；  增量可以回流；  连接配置可快速切换；  应用兼容新旧格式；  回滚时间在窗口内。超过回滚窗口后，应前向修复，不能强行回退造成数据丢失。27.10 常见问题            问题      处理                  驱动不兼容      先升级驱动和测试              复制延迟升高      暂停升级或批处理              回滚受限      检查 FCV 和持久特性              索引构建慢      控制批次和资源              迁移后路由错      校验分片元数据              数据抽样不一致      全量对账并暂停切换      本章小结升级和迁移的成功关键不是执行命令，而是兼容性验证、备份、灰度、监控验证和回滚设计。副本集滚动升级要保护多数派，分片升级要保护元数据，数据模型迁移要长期兼容新旧格式。思考题  为什么升级前必须检查驱动版本？  FCV 设置为什么影响回滚？  分片升级为什么要先处理 Config Server？  数据模型迁移如何避免停机？  什么时候应该选择前向修复而不是回滚？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。容量规划把业务增长翻译为存储、内存、网络、连接、索引、分片和备份容量。MongoDB 特别需要关注工作集：数据总量能很大，但常用数据和索引必须尽量留内存。26.1 输入数据收集：            项目      说明                  文档速率      每秒插入、更新、删除              文档大小      平均值和 P99              读 QPS      高频查询和聚合              工作集      常用数据和索引              保留周期      在线、归档、备份              峰值系数      促销、重试、回放              副本数      每份数据复制              索引数量      写放大和空间      26.2 存储容量估算：logical bytes = docs × avg doc sizeraw bytes = logical bytes × compression factorindex bytes = sum(index size)replica storage = raw bytes + index bytesretention storage = daily growth × retention days再预留：  系统和日志空间；  journal 和 checkpoint 空间；  索引重建空间；  chunk 迁移临时空间；  备份恢复窗口；  突发增长；  至少一个扩容周期。磁盘水位建议 70% 开始评估扩容，90% 进入应急处理。26.3 内存容量工作集包括：  热点集合页；  热点索引；  排序和聚合；  连接缓冲；  查询执行状态。判断：working set + system + mongod overhead &lt; RAM观察：db.serverStatus().wiredTiger.cache如果缓存未命中和磁盘读持续增长，说明内存或工作集需要调整。26.4 连接与线程估算：connections = app instances × pool size示例：50 app pods × 20 connections = 1000 connections容量还要看：  每连接内存和线程开销；  mongod maxIncomingConnections；  mongos 连接扇出；  分片数；  操作执行队列；  TLS 握手开销。分片集群中每个 mongos 可能连接每个 shard，连接模型必须提前评估。26.5 写入容量压测指标：insert / update / delete TPSP99 operation latencywrite conflictsjournal latencycache dirty pagesdisk throughputreplication lag规划原则：  峰值低于压测安全水位；  留出副本同步余量；  事务要短；  热点文档单独治理；  索引数量受控；  批处理错峰。26.6 读容量按查询分层：            查询类型      容量策略                  主键或索引点查      常规容量              列表分页      排序索引              聚合报表      Hidden Secondary 或分析库              全文搜索      搜索服务              冷数据查询      归档系统      Secondary 读可以扩展读吞吐，但要考虑复制延迟和 Secondary 磁盘能力。26.7 分片容量评估：single shard safe capacityrequired shards = ceil(total peak load / shard safe load)示例：peak write = 120,000 ops/ssingle shard safe = 30,000 ops/srequired shards = 4还要验证：  分片键能均匀分布；  高频查询能定向；  每个 shard 的数据量；  chunk 迁移成本；  Config Server 容量；  mongos 数量；  跨 shard 事务比例。26.8 备份容量规划：  全量备份大小；  增量或 oplog 窗口；  保留天数；  异地副本；  恢复临时集群空间；  备份网络带宽；  恢复 RTO。分片集群要同时备份 Config Server 与各 shard，不能只算业务数据总量。26.9 容量评审上线前回答：  一年数据增长多少；  热点集合大小多少；  索引总大小多少；  P99 目标多少；  峰值读写多少；  工作集是否在内存；  磁盘水位何时到达 70%；  分片扩容触发条件；  备份空间是否足够；  恢复演练多久一次。本章小结MongoDB 容量规划的核心是工作集、索引空间、写入放大、复制开销和分片路由。磁盘总量不代表性能能力，常用数据和索引是否能留在内存更关键。分片与备份容量应一起规划。思考题  为什么工作集比总数据量更关键？  副本数如何影响容量？  mongos 连接扇出如何估算？  分片数量如何从压测推导？  备份容量为什么包含 Config Server？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。性能调优目标是满足业务 SLA，而不是追求单条查询极限。MongoDB 调优通常从查询和索引开始，再处理 Schema、cache、磁盘、复制和分片。25.1 调优流程define latency targets  -&gt; collect baseline  -&gt; find slow operations  -&gt; explain query  -&gt; fix indexes / schema  -&gt; tune workload  -&gt; test under concurrency  -&gt; monitor result目标示例：            指标      目标                  订单读 P99      &lt; 20 ms              订单写 P99      &lt; 50 ms              副本延迟      &lt; 1 s              复制延迟峰值      &lt; 5 s              慢查询      &lt; 0.1%      25.2 查询优化优先做：  高频查询加复合索引；  排序字段进索引；  投影减少返回；  游标分页；  $match 前移；  控制 $in 大小；  避免非前缀正则；  高频搜索使用专用索引。验证：db.orders.find({  user_id: "u_1001",  status: "PAID"}).explain("executionStats")目标：IXSCANnReturned ≈ keysExaminedno SORT25.3 写入优化优化点：  减少无效索引；  批量写入控制批次；  拆小事务；  避免热点文档；  避免超大数组更新；  延迟 Secondary 负载；  控制文档增长；  大字段拆分；  使用合适的写关注。批量写入：db.orders.bulkWrite([  { insertOne: { document: { _id: "o_1", status: "CREATED" } } },  { insertOne: { document: { _id: "o_2", status: "CREATED" } } }], { ordered: false })25.4 Cache 调优查看：db.serverStatus().wiredTiger.cache处理：            信号      优化                  pages read 高      增内存、缩工作集              dirty pages 高      降写入、检查磁盘              evict waiting      降低并发或扩 cache              checkpoint 慢      减少脏页和索引      更有效的方向通常是减少工作集：拆冷数据、投影、减少索引和控制文档大小。25.5 磁盘与文件系统建议：  数据盘使用 SSD 或 NVMe；  监控 util、latency、IOPS；  预留空间；  journal 与数据目录按官方建议部署；  避免与重 IO 服务混部；  定期验证快照性能；  关注磁盘队列和 iowait。25.6 副本集性能关注：  Primary 写入延迟；  Secondary 复制延迟；  oplog 窗口；  Secondary 读负载；  索引构建；  网络带宽；  心跳和选举健康。优化：  Secondary 只承担可延迟读；  报表使用 Hidden Secondary；  避免同时重建所有节点索引；  大事务拆小；  跨机房部署评估同步延迟。25.7 分片性能优化：  高频查询带分片键；  避免广播查询；  分片键分布均匀；  控制 jumbo chunk；  balancer 低峰运行；  mongos 足够实例；  减少跨 shard 事务；  监控每 shard 负载。25.8 应用侧优化  使用连接池；  控制连接数；  设置查询超时；  使用读写关注策略；  请求携带 trace ID；  幂等重试；  避免每请求全量查询；  本地缓存热点数据；  批处理错峰；  熔断非核心查询。25.9 压测压测场景：  读写混合；  热点用户；  聚合报表；  大批量导入；  Secondary 读；  分片迁移；  Primary 切换；  索引上线；  长稳测试。记录：throughputp95 / p99 latencyerror ratecache metricsdisk metricsreplication lagapplication result25.10 反模式            反模式      后果                  只看平均延迟      掩盖长尾              索引越多越好      写入变慢              事务包住外部调用      长事务              Secondary 读解决一切      读旧数据              分片解决慢查询      广播更慢              盲目调 cache      抢占系统内存              生产直接建大索引      影响线上      本章小结MongoDB 性能优化通常先修查询、索引和 Schema，再治理工作集、磁盘和复制延迟。写入端关注热点和索引成本，分片端关注路由和均衡。所有优化都必须通过并发压测和上线后监控验证。思考题  为什么先优化查询再扩容？  工作集大于内存有什么表现？  索引过多的写入代价是什么？  分片为什么可能让慢查询更慢？  压测为什么必须包含长稳阶段？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。排障原则：先固定影响面和时间线，再定位层级，最后做可回滚止损。不要在未看指标前重启、切主或修改参数，避免破坏证据。24.1 排查框架confirm impact  -&gt; collect timeline  -&gt; check topology  -&gt; check resources  -&gt; check slow queries  -&gt; check application  -&gt; mitigate  -&gt; verify  -&gt; review需要记录：  开始和恢复时间；  环境、集群、数据库；  错误率、延迟、可用性；  发布和配置变更；  依赖服务状态；  最终根因和动作。24.2 连接失败常见错误：            错误      排查                  Connection refused      进程退出、端口、bindIp              Authentication failed      用户、密码、authSource              Timeout      网络或 DNS              No primary      选举、多数丢失              Not primary      写入 Secondary              ReplicaSetNoPrimary      拓扑发现异常      检查：mongosh --host mongo1 --port 27017 -u admin -p --authenticationDatabase adminss -lntp | grep 2701724.3 慢查询定位：db.setProfilingLevel(1, { slowms: 100, sampleRate: 1 })查看：db.system.profile  .find()  .sort({ ts: -1 })  .limit(10)执行计划：db.orders.find({ user_id: "u_1001" })  .explain("executionStats")常见原因：  COLLSCAN；  索引选择性差；  内存排序；  返回大文档；  聚合数据放大；  cache 压力；  磁盘瓶颈。24.4 写入延迟高检查：operation write latencywrite conflictstransaction durationcache dirty pagesdisk utiljournal latencyreplication lagindex count处理：  控制写入速率；  拆小事务；  减少热点文档冲突；  检查索引数量；  评估磁盘性能；  降低 Secondary 压力；  扩展分片。24.5 Cache 与磁盘查看：db.serverStatus().wiredTiger.cache典型信号：            信号      可能                  pages read into cache 高      工作集大于内存              dirty pages 高      写入过快或 checkpoint 慢              pages evicted 高      cache 竞争              disk util 高      IO 瓶颈              checkpoint duration 长      脏页或磁盘压力      止损：  停止重查询；  限制批处理；  扩容内存；  归档冷数据；  增加分片；  优化索引和工作集。24.6 复制延迟查看：rs.printSecondaryReplicationInfo()rs.status()原因：  Secondary 磁盘慢；  网络带宽不足；  重查询影响；  索引构建；  大量写入；  大事务；  初始同步。处理：  停止 Secondary 重负载；  降低写入；  修复网络；  等待索引完成；  必要时重新同步；  切读回 Primary。24.7 选举异常检查：rs.status()db.hello()原因：  Primary 故障；  网络分区；  多数成员不可用；  priority 配置错误；  节点资源异常；  心跳超时。处理：  恢复多数成员；  检查网络；  恢复磁盘和进程；  手动 stepDown 前确认目标健康；  保留选举日志。24.8 正在执行的长时间操作查看：db.currentOp({  active: true,  secs_running: { $gte: 60 }})终止：db.killOp(opid)注意：  确认不是事务协调关键操作；  记录操作来源；  应用端同时取消；  强杀是止损，不是根因修复；  必须追溯为什么无超时。24.9 分片问题常见现象：            现象      排查                  查询广播      查询不带分片键              某 shard 慢      数据倾斜、热点、资源              迁移卡住      网络、目标空间、元数据              mongos 错误      路由缓存、Config 状态              chunk jumbo      分片键频率过高      查看：sh.status()use configdb.chunks.find({ ns: "shop.orders" })24.10 数据不一致疑云分层确认：application sent?  -&gt; write ack?  -&gt; primary visible?  -&gt; majority committed?  -&gt; secondary read?  -&gt; application cache?  -&gt; downstream index?处理前先保存：  文档 _id；  请求 ID；  写关注和读偏好；  操作时间；  oplog 或审计；  应用日志。24.11 应急预案            故障      止损                  无 Primary      恢复多数成员、降级写              磁盘将满      停批处理、扩容、清理临时文件              cache 压力      停重查询、限流、扩内存              复制延迟      Secondary 摘读、恢复资源              慢查询风暴      限流、kill、启用缓存              误删除      停写、备份、时间点恢复              分片迁移压力      暂停 balancer      本章小结MongoDB 故障通常由查询、索引、cache、磁盘、复制、选举或分片路由共同造成。先用 serverStatus、rs.status、Profiler、explain 和系统指标固定证据，再做可回滚动作。恢复后必须验证业务数据和复盘。思考题  为什么排障初期不建议直接重启？  secs_running 高的操作如何处理？  复制延迟的常见原因有哪些？  广播查询为什么代价高？  误删除后第一步应该做什么？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 监控要同时看数据库内部状态和应用视角延迟。核心内容包括可用性、复制、资源、查询、缓存、连接、事务和业务指标。23.1 指标来源常用命令：db.serverStatus()db.hello()rs.status()rs.printSecondaryReplicationInfo()db.printCollectionStats()db.currentOp()聚合样例：db.serverStatus().connectionsdb.serverStatus().opcountersdb.serverStatus().wiredTiger.cachedb.serverStatus().metrics.repl23.2 可用性指标：            指标      含义                  member_state      Primary / Secondary 等              elections_total      选举次数              replica_set_members      成员数量              voting_majority_healthy      多数健康              mongos_alive      路由可用              config_server_healthy      元数据服务健康      告警：  无 Primary；  多数成员不可达；  频繁选举；  成员状态异常；  mongos 或 Config Server 不可用。23.3 复制与分片副本集：replication_lag_secondsoplog_window_hoursoplog_ratemember_healthrollback_events分片：chunks_per_sharddata_size_per_shardbalancer_runningmigration_durationmigration_failuresscatter_gather_queries告警：  复制延迟持续增长；  oplog 窗口过短；  shard 数据倾斜；  迁移长期运行；  广播查询占比高。23.4 资源指标系统层：cpu_usagecpu_iowaitmemory_usageswap_usagedisk_usagedisk_io_utildisk_read_bytesdisk_write_bytesnetwork_bytesfile_descriptorsWiredTiger：cache_bytescache_dirty_bytespages_read_into_cachepages_written_from_cachepages_evictedeviction_queue_lengthcheckpoint_duration判断：  cache 未命中高说明工作集压力大；  dirty pages 持续高说明写入或 checkpoint 压力；  iowait 高说明磁盘瓶颈；  swap 出现说明内存规划异常；  磁盘水位高影响写入和恢复。23.5 查询指标operation_insertoperation_queryoperation_updateoperation_deleteoperation_getmoreoperation_commandquery_latency_msslow_query_countscanned_returned_ratiocollscan_countsort_stage_countindex_size_bytes慢查询来源：  Profiler；  慢日志；  APM；  应用 trace；  数据库面板。23.6 连接与队列connections_currentconnections_availableconnections_createdthread_queueactive_clients_readersactive_clients_writerscurrent_op_runningcurrent_op_blocked常见问题：            现象      原因                  连接暴涨      无连接池或实例频繁重建              队列增长      慢查询、锁或资源瓶颈              可用连接低      maxIncomingConnections 过低              读写客户端堆积      磁盘或 cache 压力      23.7 事务指标transactions_totaltransactions_committransactions_aborttransaction_duration_mswrite_conflictstransactions_currentlock_wait_time告警：  事务 P99 超标；  回滚率高；  写冲突集中；  当前事务持续增长；  事务超时增加。23.8 仪表盘设计推荐视图：  拓扑总览；  Primary 与复制延迟；  Shard 分布；  慢查询列表；  Cache 与磁盘；  连接与队列；  索引空间；  事务冲突；  应用侧延迟；  业务成功率。数据库指标必须与应用请求指标放在同一时间线。23.9 告警分级            级别      场景                  P0      无 Primary、多数丢失、核心写入失败              P1      复制延迟过高、磁盘将满、核心查询超时              P2      cache 压力、慢查询增长、shard 倾斜              P3      非核心集合异常      每条告警包含：  环境；  集群和成员；  影响范围；  当前值；  处理入口；  Runbook 链接。本章小结MongoDB 监控要覆盖拓扑、复制、分片、cache、磁盘、查询、连接和事务。判断性能时应把 serverStatus、慢查询、执行计划和应用延迟关联起来。告警关注趋势和影响范围，而不是孤立阈值。思考题  复制延迟高对 Secondary 读有什么影响？  cache 脏页持续高说明什么？  为什么必须监控 oplog 窗口？  shard 数据倾斜会带来什么？  如何设计数据库与应用联合视角？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。备份的价值只有通过恢复演练才能证明。MongoDB 备份必须同时考虑数据文件、副本集 oplog、分片 Config Server、事务一致性点和集群元数据。22.1 目标先定义：            目标      问题                  RPO      最多丢多久数据              RTO      多久恢复服务              范围      哪些库、集合、元数据              场景      误删除、节点损坏、机房故障              恢复位置      原集群、临时集群、时间点      示例：core collections RPO &lt;= 5 minutesrestore rehearsal RTO &lt;= 30 minutesaudit collections retain 365 days22.2 备份对象副本集：  数据文件；  oplog；  用户和角色；  索引定义；  集合配置；  mongod 配置；  密钥和证书配置说明。分片集群额外需要：  Config Server；  shard 拓扑；  chunk 元数据；  zone 配置；  mongos 配置；  balancer 状态。只备份业务集合而不备份元数据，恢复后路由可能不完整。22.3 mongodump逻辑备份：mongodump \  --uri="mongodb://backup_user:password@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&amp;authSource=admin" \  --db=shop \  --out=/backup/20260825 \  --gzip恢复：mongorestore \  --uri="mongodb://admin:password@restore-target:27017/admin" \  --db=shop \  --dir=/backup/20260825/shop \  --gzip \  --drop适合：  小数据集；  单库单集合；  跨版本导出；  开发环境。限制：  大集合耗时长；  默认不是持续时间点备份；  恢复需要重建索引；  对在线集群有读压力；  不适合超大规模全量。22.4 快照备份文件或云盘快照要求捕获一致时间点：flush or coordinate filesystem snapshot  -&gt; snapshot data volume  -&gt; include journal  -&gt; record oplog position常见策略：  备份节点快照；  云盘崩溃一致性快照；  文件系统冻结；  与官方工具结合；  分片集群统一时间点。优点是恢复快、适合大数据量；限制是依赖存储平台和拓扑一致性。22.5 延迟副本Delayed Secondary 可以提供逻辑延迟：cfg = rs.conf()cfg.members[2].hidden = truecfg.members[2].priority = 0cfg.members[2].secondaryDelaySecs = 3600rs.reconfig(cfg)适合：  误更新恢复；  快速查看历史状态；  应急取证。限制：  不是备份；  硬件故障无法保护；  延迟窗口有限；  不能替代异地备份。22.6 时间点恢复思路：restore snapshot at T0  -&gt; replay oplog to T1  -&gt; stop before wrong operation要求：  快照时间点记录；  oplog 覆盖 T0 到 T1；  恢复目标时间明确；  在隔离环境执行；  恢复结果通过业务校验。误删除恢复不要直接覆盖生产，应先恢复临时集群导出数据。22.7 恢复演练流程：provision isolated cluster  -&gt; restore metadata  -&gt; restore data  -&gt; start service  -&gt; verify users  -&gt; verify indexes  -&gt; count collections  -&gt; sample business keys  -&gt; test read and write  -&gt; measure RTO验收：  文档数；  索引数；  用户角色；  副本集状态；  分片路由；  关键业务查询；  恢复时间；  备份完整性日志。22.8 备份安全要求：  备份文件加密；  访问权限最小化；  与生产不同账号管理；  异地保存；  传输加密；  保留策略合规；  删除审批；  备份系统纳入审计。22.9 常见问题            问题      原因                  恢复后无用户      未备份 admin 库              分片路由错误      Config Server 未恢复              索引缺失      mongorestore 元数据异常              时间点无法恢复      oplog 覆盖不足              快照不一致      journal 未包含或时间点错              恢复慢      数据量大、索引重建      本章小结MongoDB 备份要根据 RPO/RTO 选择 mongodump、快照、持续备份或组合方案。分片集群必须备份 Config 元数据，恢复必须演练并验证路由、用户、索引和业务数据。延迟副本是应急手段，不是完整备份。思考题  为什么分片集群必须备份 Config Server？  mongodump 适合什么规模？  快照为什么要包含 journal？  延迟副本为什么不能替代备份？  时间点恢复需要哪些条件？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 常保存用户资料、业务订单、日志和敏感行为数据。安全治理要覆盖网络边界、认证授权、TLS、字段级敏感信息、审计和密钥生命周期。21.1 访问控制启用认证后，所有客户端必须使用用户连接。创建管理员：use admindb.createUser({  user: "admin",  pwd: passwordPrompt(),  roles: [{ role: "userAdminAnyDatabase", db: "admin" }]})创建业务用户：use shopdb.createUser({  user: "shop_app",  pwd: passwordPrompt(),  roles: [    { role: "readWrite", db: "shop" }  ]})不要让所有服务共享管理员账号。21.2 内置角色            角色      权限                  read      读指定库              readWrite      读写指定库              dbAdmin      管理指定库              userAdmin      管理指定库用户              clusterMonitor      查看集群监控              backup / restore      备份恢复              root      超级权限      原则：  业务应用 readWrite；  报表只读；  运维按操作分段授权；  监控 clusterMonitor；  root 仅 break-glass。21.3 自定义角色限制集合：use shopdb.createRole({  role: "orderReader",  privileges: [    {      resource: { db: "shop", collection: "orders" },      actions: ["find"]    }  ],  roles: []})适用：  只读某集合；  只允许特定操作；  审计查询；  内部平台数据视图。21.4 网络隔离生产建议：  mongod 只监听内网地址；  不把 27017 暴露公网；  使用防火墙或安全组；  按环境隔离 VPC；  mongos 和应用同受控网络；  运维通过堡垒机；  分片组件之间限制来源。查看监听：ss -lntp | grep 2701721.5 TLS连接串示例：mongodb://user:pass@mongo1,mongo2,mongo3/shop?replicaSet=rs0&amp;tls=true&amp;authSource=admin启用前验证：  证书有效期；  证书 SAN；  CA 分发；  客户端驱动兼容；  性能影响；  轮转流程；  回滚方式。21.6 字段级安全MongoDB 的权限通常到集合或库级别，字段级访问需要应用层或查询视图治理。示例只读视图：db.createView("user_public", "users", [  { $project: {      _id: 1,      nickname: 1,      city: 1  }}])原则：  密码只保存哈希；  手机号和身份证脱敏；  token 不明文入库；  大敏感数据加密；  应用日志同步脱敏；  查询导出需审批。21.7 审计企业版提供审计能力，具体能力与版本和发行版有关。至少应审计：  登录成功和失败；  用户与角色变更；  权限变更；  集合和索引管理；  备份恢复；  高危运维命令；  敏感集合查询和导出。应用层也应保存业务操作审计。21.8 密钥管理要求：  密码和证书不写入代码仓库；  使用 KMS、Secret 或云托管身份；  定期轮转；  不同环境不同凭据；  副本集 keyfile 权限受控；  泄露后立即吊销；  记录凭据使用。检查文件权限：ls -l /etc/mongo-keyfile21.9 注入与查询安全驱动使用结构化查询，不拼接字符串。应用层：  不接受用户传入任意操作符；  限制可查询字段；  限制返回条数；  查询超时；  敏感字段过滤；  导出限流。示例拒绝用户直接提交 $where。21.10 常见风险            风险      处理                  未启用认证      立即启用并隔离网络              公网暴露      关闭公网和安全组              root 共享      按服务拆分账号              明文传输      启用 TLS              备份未加密      加密存储并控权限              无审计      建立审计流程              离职未回收      定期权限复核      本章小结MongoDB 安全从网络隔离和认证授权开始，再到 TLS、最小权限、字段脱敏、审计和密钥治理。集合级权限不能完全替代应用层数据安全，两者需要配合。思考题  为什么业务应用不应使用 root？  自定义角色适合什么场景？  视图能否替代字段级权限？  TLS 上线前要验证什么？  哪些操作必须进入审计？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 的一致性由写关注、读关注和读偏好共同决定。理解三者组合，才能回答“这条数据能不能读到”“读到的是否是多数已提交”“是否可能比 Primary 旧”。20.1 三层控制            机制      问题                  Write Concern      写到多少节点才返回成功              Read Concern      读哪个一致性级别的数据              Read Preference      从哪个成员读      示例：db.orders.find(  { user_id: "u_1001" }).readPref("primary").readConcern("majority")20.2 Write Concern写关注：db.orders.insertOne(  { _id: "o_1", status: "CREATED" },  { writeConcern: { w: "majority", j: true, wtimeout: 3000 } })            参数      说明                  w: 0      不等待确认，风险高              w: 1      Primary 确认              w: "majority"      多数成员确认              j: true      等待 journal              wtimeout      超时时间      重要数据建议 majority；日志、行为事件可按成本选择，但要有补偿和对账。20.3 Read Concern常见级别：            级别      语义                  local      返回本节点已应用的数据              majority      返回多数节点已提交的数据              linearizable      强一致线性读              snapshot      事务快照              available      可用性优先，分片场景语义需注意      local 可能读到后续回滚的数据；majority 降低回滚读风险；linearizable 延迟和限制更高。20.4 Read Preference            模式      行为                  primary      只读 Primary              primaryPreferred      优先 Primary              secondary      只读 Secondary              secondaryPreferred      优先 Secondary              nearest      最低延迟成员      Secondary 读不等于弱一致，最终语义还要看 read concern；但复制延迟会让数据版本较旧。20.5 因果一致性客户端会话可以维护因果令牌，让后续操作看到之前操作的影响：const session = db.getMongo().startSession({ causalConsistency: true });const orders = session.getDatabase("shop").orders;orders.insertOne({ _id: "o_1", status: "CREATED" });orders.find({ _id: "o_1" });session.endSession();适用：  写后读；  会话内单调读；  分页延续；  多步配置变更。因果一致性不等于全局线性一致。20.6 写后读场景支付后立即查询：write majority  -&gt; read primary + majority配置：db.orders.insertOne(  { _id: "o_1", status: "PAID" },  { writeConcern: { w: "majority" } })db.orders.find({ _id: "o_1" }).readPref("primary")如果使用 Secondary 读，用户可能看不到刚支付的状态，需要产品提示或强制 Primary。20.7 报表读取报表可以读 Hidden Secondary：read preference: secondaryread concern: majority特点：  不影响主业务 Primary；  可能滞后；  重查询仍会影响该 Secondary；  应有资源隔离和超时；  跨分片聚合由 mongos 合并。20.8 分片一致性分片下还要注意：  不带分片键的查询可能读取多个 shard；  各 shard 复制进度可能不同；  majority 读降低回滚风险；  事务需要快照和提交协调；  mongos 拓扑状态影响路由。对账和全局报表应明确时间点。20.9 一致性权衡            需求      建议                  支付状态      primary + majority              用户资料      primary 或 secondaryPreferred              报表      secondary + majority              推荐缓存      nearest + local              审计追踪      majority 写入              跨服务流程      业务状态机 + 对账      不要为所有请求使用最高一致性，也不要默认所有 Secondary 读都能接受。20.10 超时与重试写关注超时后，写入可能已经发生：timeout -&gt; unknown result处理方式：  保存请求 ID；  查询业务状态；  幂等重试；  记录不确定结果；  告警；  不盲目重复不可幂等写入。本章小结一致性不是单一参数，而是写关注、读关注和读偏好的组合。核心交易使用 majority 与 Primary 读，分析报表可以读 Secondary，但仍要定义滞后容忍度。所有超时都要按“结果未知”处理。思考题  w: 1 和 w: majority 的核心差异是什么？  local 读可能读到什么风险数据？  Read Preference 是否决定数据提交级别？  因果一致性适合什么场景？  写关注超时后为什么不能直接认定失败？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 支持单文档原子操作和多文档 ACID 事务。单文档原子性是重要优势，多文档事务则适合跨集合或跨文档的一致性边界，但会占用资源、增加冲突，应保持短小。19.1 单文档原子性以下更新在单个文档内原子完成：db.orders.updateOne(  { _id: "o_10001", status: "WAIT_PAY" },  {    $set: { status: "PAID" },    $inc: { version: 1 },    $push: { status_history: { status: "PAID", at: new Date() } }  })设计建议：  把强一致的小边界放进同一文档；  用条件更新实现状态机；  用 $inc 更新计数；  用版本号乐观并发；  避免读改写竞争。19.2 多文档事务示例：const session = db.getMongo().startSession();try {  session.startTransaction({    readConcern: { level: "snapshot" },    writeConcern: { w: "majority" }  });  const orders = session.getDatabase("shop").orders;  const accounts = session.getDatabase("shop").accounts;  orders.updateOne(    { _id: "o_10001", status: "WAIT_PAY" },    { $set: { status: "PAID" } }  );  accounts.updateOne(    { _id: "a_1001", balance: { $gte: NumberDecimal("100.00") } },    { $inc: { balance: NumberDecimal("-100.00") } }  );  session.commitTransaction();} catch (error) {  session.abortTransaction();  throw error;} finally {  session.endSession();}19.3 事务要求多文档事务要求：  副本集或分片集群；  使用会话；  数据和目录在支持事务的存储引擎；  分片集群需要可路由的分片键或目标操作；  事务超时和锁资源可控。单机 mongod 不应承载生产多文档事务。19.4 事务边界适合事务：  转账；  订单与库存强一致变更；  多表状态联动；  配置组变更；  少量跨集合写入。不适合事务：  调用外部 HTTP；  大批量导入；  长时间计算；  等待用户输入；  跨多个微服务的最终一致流程。外部 IO 不应放在数据库事务内。19.5 读关注与写关注事务示例：session.startTransaction({  readConcern: { level: "snapshot" },  writeConcern: { w: "majority", wtimeout: 5000 }})组合建议：            场景      组合                  交易核心      snapshot + majority              普通业务      snapshot 或 local + majority              内部任务      local + w:1              对账任务      snapshot      一致性越强，故障和网络异常下的等待成本越高。19.6 重试与错误常见错误类型：            错误      处理                  TransientTransactionError      可重开事务              UnknownTransactionCommitResult      可重试提交              写冲突      重试或调整模型              超时      拆小事务              分片路由错误      检查分片键      推荐驱动使用事务 API 的便捷重试能力；手写时必须区分事务重试和提交重试。19.7 事务与索引事务内查询也要有索引：filter without index  -&gt; scan many documents  -&gt; hold resources longer  -&gt; more conflicts检查：  每个事务内更新条件；  唯一索引；  分片键；  执行计划；  冲突率。19.8 分片事务跨 shard 事务比单 shard 事务成本更高：  参与者更多；  协调时间更长；  网络故障影响更大；  需要提交协议；  更容易超时。设计建议：  事务涉及的文档尽量同 shard；  复合分片键覆盖事务实体；  跨 shard 事务有限使用；  监控事务时长；  长流程改最终一致。19.9 替代方案不是所有一致性问题都要用事务：            需求      方案                  单对象状态流转      条件更新              重复请求      唯一索引              计数      $inc              跨服务长流程      Saga + 补偿              搜索索引同步      Change Stream + 幂等              对账      离线核对      事务适合短小一致边界，不适合长生命周期业务流程。19.10 监控指标transactions_totaltransactions_commit_totaltransactions_abort_totaltransaction_duration_mstransaction_write_conflicts_totaltransactions_currenttransaction_open_cursorslock_queue_time_ms告警：  事务时长 P99 超标；  冲突率升高；  回滚率升高；  当前事务数持续增长；  锁等待集中。本章小结MongoDB 的第一选择是利用单文档原子性和条件更新。多文档事务适合少量强一致操作，必须短小、有索引、明确读写关注并处理重试。长流程和跨服务流程应使用状态机、补偿和事件驱动。思考题  为什么单文档更新更适合状态机？  多文档事务中为什么不能调用外部 HTTP？  snapshot 读关注解决什么问题？  哪些错误可以安全重试事务？  分片事务为什么要减少跨 shard？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。分片键决定数据分布、查询路由、写入热点、chunk 能否分裂和事务范围。它是 MongoDB 分片架构中最难修改的决策，必须在业务建模阶段认真评估。18.1 设计目标一个好的分片键应满足：  高基数：可取值足够多；  低频率：单值数据不集中；  单调性可控：递增字段不把写入集中到一个 shard；  高频查询包含分片键；  写入分布均匀；  chunk 可以分裂；  事务跨 shard 少；  业务语义清晰。18.2 基数与频率低基数示例：shard key = regionpossible values = north, south, east, west只有四个值，最多有效分布到有限 chunk。高基数示例：shard key = user_idmillions of values频率问题：user_id = null 占 80% documents即使字段基数高，某个值占比过高仍会形成 jumbo chunk。18.3 单调递增键的问题使用自增 ID 或时间作为范围分片键：time: 10:00 -&gt; shard lasttime: 10:01 -&gt; shard lasttime: 10:02 -&gt; shard last新写入集中到最后一个 chunk，形成热点。替代方案：  哈希分片；  复合前缀打散；  用户 ID 或设备 ID；  业务分区键 + 时间；  粗粒度桶 + 前缀随机。18.4 哈希分片创建：sh.shardCollection("shop.orders", { user_id: "hashed" })优点：  写入分布均匀；  对递增键友好；  减少热点。限制：  范围查询通常广播；  相邻数据不在同一 chunk；  仍然依赖字段基数；  热点单值无法解决。18.5 复合分片键查询：db.orders.find({  tenant_id: "t_100",  created_at: { $gte: start, $lt: end }})分片键：sh.shardCollection("multi.orders", { tenant_id: 1, user_id: 1 })优点：  支持租户定向；  每个租户内部可分布；  写入更均匀；  便于 zone 隔离。风险：  大租户仍可能热点；  查询不带前缀无法定向；  事务范围可能跨 shard；  chunk 边界更复杂。18.6 常见业务分片键            业务      候选                  订单      tenant_id + order_id              用户行为      user_id + event_time              IoT 数据      device_id + timestamp              聊天消息      conversation_id + message_id              审计日志      service_id + request_id              商品评价      sku_id + review_id              文档服务      tenant_id + doc_id      尽量避免只用时间、自增 ID 或状态这类低基数字段。18.7 热点识别信号：  单 shard 写入明显高于其他；  某 chunk 持续过大；  集合写入延迟高但总量不高；  balancer 无法平衡；  cache 压力集中在单 shard；  某个分片键值文档巨大。查看：sh.status()db.orders.aggregate([  { $group: { _id: "$tenant_id", count: { $sum: 1 } } },  { $sort: { count: -1 } },  { $limit: 20 }])18.8 分片键变更历史版本中分片键基本不可修改；较新版本提供的能力和限制随版本变化。生产操作前必须确认：  当前版本是否支持；  集合是否满足条件；  是否需要停写或低峰；  磁盘空间是否足够；  查询是否全部更新；  回滚窗口；  官方操作说明。很多团队更稳妥的做法是新建集合、双写迁移、验证后切换。18.9 设计验证用真实数据验证：top 20 querieswrite distribution simulationcardinality statisticskey frequency distributionchunk split simulationcross-shard transaction rate压测：  写入吞吐；  P99 延迟；  查询路由；  balancer 活动；  cache 压力；  网络流量。18.10 反模式            反模式      后果                  时间范围键      写热点              状态字段键      只有少数值              空值占比高      jumbo chunk              查询不带分片键      scatter-gather              大租户单独键      租户热点              随意改分片键      迁移风险高              过早分片      复杂度收益不成比例      本章小结分片键要同时考虑基数、频率、写入分布和查询路由。范围分片利于范围查询但易热点，哈希分片分布均匀但范围查询代价高，复合分片键常用于租户与实体组合。设计后必须用真实数据分布和查询压测验证。思考题  为什么高基数不等于没有热点？  自增时间键为什么容易热点？  哈希分片的代价是什么？  复合分片键的前缀约束是什么？  如何发现大租户热点？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。Chunk 迁移由 Balancer 触发，用于把数据块从负载或数据量较高的 shard 移到较低 shard。迁移影响磁盘 IO、网络、oplog、缓存和查询延迟，必须通过窗口控制和监控治理。17.1 为什么迁移触发原因：  shard 之间 chunk 数不均衡；  数据量差异超过阈值；  新增 shard；  分片键热点变化；  手动移动 chunk；  zone 策略调整。目标：shard 1 100 chunksshard 2 100 chunksshard 3 100 chunks17.2 Balancer查看：sh.status()sh.isBalancerRunning()停止：sh.stopBalancer()启动：sh.startBalancer()设置均衡窗口：db.settings.updateOne(  { _id: "balancer" },  { $set: { activeWindow: { start: "02:00", stop: "06:00" } } },  { upsert: true })17.3 迁移流程balancer selects chunk  -&gt; source opens migration  -&gt; target clones chunk data  -&gt; target catches up changes  -&gt; config metadata commit  -&gt; source deletes data阶段：            阶段      影响                  clone      源和目标磁盘、网络              catch up      复制增量修改              commit      config 元数据变更              delete      源 shard 删除旧数据      删除阶段可能带来明显 IO 压力。17.4 迁移控制常用做法：  设置低峰窗口；  限制并发迁移；  新 shard 灰度加入；  控制大批量导入节奏；  迁移期间避免重建索引；  观察复制延迟；  磁盘预留空间。新增 shard 后Balancer 会逐步迁移数据，不要期望瞬间完成。17.5 手动迁移查看 chunk：use configdb.chunks.find({ ns: "shop.orders" }).sort({ min: 1 }).limit(5)移动示例命令名称和参数随版本变化，应使用当前版本文档确认。生产手工移动前必须：  记录 chunk 范围；  确认目标 shard 空间；  停止业务大批量操作；  预估迁移时间；  保留回滚方案。17.6 Zone 与数据局部性创建 zone：sh.addShardTag("shard-us", "us")关联范围：sh.addTagRange(  "shop.orders",  { region: "us", user_id: MinKey },  { region: "us", user_id: MaxKey },  "us")适用：  数据合规驻留；  就近访问；  冷热 shard 隔离；  业务域隔离。Zone 设计错误可能导致数据无法迁移或分布异常。17.7 迁移与备份迁移会带来挑战：  快照时可能存在进行中的迁移；  Config 元数据必须与 shard 数据对应；  恢复演练要验证路由；  混合备份需要时间点一致性；  恢复后观察 chunk 分布。建议：  备份窗口避开均衡窗口；  备份 Config Server；  使用官方备份机制或经过验证的快照流程；  保留集群拓扑版本。17.8 常见问题            问题      排查                  迁移一直进行      数据倾斜、chunk 太大、写入持续              查询延迟升高      迁移 IO、缓存污染              复制延迟升高      Secondary 追加变更              新 shard 没数据      Balancer 停止或窗口未到              删除阶段磁盘高      源 shard 清理              元数据不一致      config 状态、迁移失败      17.9 监控指标balancer_runningchunks_totalchunks_per_sharddata_size_per_shardmigrations_in_progressmigration_duration_msmigration_failure_totalshard_disk_usageshard_cache_pressure告警：  迁移长期卡住；  shard 数据持续倾斜；  迁移失败率高；  迁移期间复制延迟超过阈值；  某个 shard 磁盘水位过高。本章小结Chunk 迁移让分片集群保持数据均衡，但迁移本身是有成本的在线操作。生产上必须配置均衡窗口，监控迁移状态、磁盘、网络和复制延迟，并在新增节点或大批量写入时控制节奏。思考题  Balancer 的目标是什么？  迁移的删除阶段为什么可能带来压力？  为什么迁移窗口要避开备份和高峰？  Zone 适合解决什么问题？  shard 数据倾斜通常由什么造成？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。分片用于将数据水平分布到多个副本集。它解决单机容量、写入吞吐和大数据集工作集限制，但也会引入路由、分布式事务、均衡和运维复杂度。是否分片，应从数据规模和增长模型出发。16.1 组件Client  -&gt; mongos     -&gt; Shard 1 Replica Set     -&gt; Shard 2 Replica Set     -&gt; Shard 3 Replica SetConfig Server Replica Set            组件      职责                  mongos      路由请求、合并结果              Shard      保存数据子集              Config Server      保存元数据和 chunk 分布              Balancer      迁移 chunk 平衡集群      16.2 什么时候分片分片前先确认瓶颈：            瓶颈      分片是否有帮助                  数据量超过单机容量      有              写入吞吐超过单机      有              工作集超过内存      有              单热点文档      无              慢查询或无索引      先优化              连接数过多      先治理应用              磁盘故障      恢复或升配      过早分片会让查询和事务复杂化，收益可能不明显。16.3 分片键分片键决定文档分布：sh.shardCollection("shop.orders", { user_id: "hashed" })范围分片：sh.shardCollection("shop.orders", { user_id: 1, created_at: 1 })            类型      特点                  范围分片      支持范围查询，易热点              哈希分片      分布均匀，范围查询需广播              复合分片      平衡查询与分布      分片键一旦选择，后续调整代价很高，必须在设计早期评估。16.4 ChunkMongoDB 将分片键空间划分为 chunk：shard 1: [minKey, user_1000)shard 2: [user_1000, user_2000)shard 3: [user_2000, maxKey]查看分布：sh.status()查看 chunk：use configdb.chunks.find({ ns: "shop.orders" }).limit(5)16.5 查询路由带分片键：db.orders.find({ user_id: "u_1001" })路由到目标 shard。不带分片键：db.orders.find({ status: "PAID" })mongos 需要广播到多个 shard，再合并结果。高频查询应包含分片键或支持定向路由。16.6 写入路由单文档写：document shard key  -&gt; target shard  -&gt; shard replica set primary不带分片键的插入在一些模式下无法路由，必须提供分片键。更新和删除如果不带分片键，也会影响路由范围。16.7 Config ServerConfig Server 保存：  集群成员；  数据库和集合分片配置；  chunk 区间；  balancer 状态；  分片拓扑。要求：  部署为副本集；  使用低延迟可靠存储；  严格控制权限；  纳入备份；  不承载业务读负载。Config Server 不可用会影响元数据操作和路由发现。16.8 mongos建议：  多实例部署；  应用配置多个 mongos；  与应用网络就近；  监控连接和请求延迟；  控制连接池；  优雅发布。mongos 无状态，扩容相对容易，但结果合并和广播查询仍会带来 CPU 与网络成本。16.9 目标分片策略设计流程：find top queries  -&gt; choose shard key  -&gt; verify cardinality  -&gt; verify write distribution  -&gt; verify target queries  -&gt; test split and balance目标：  写入均匀；  高频查询带分片键；  分片键基数足够；  单 chunk 不无限增长；  热点 key 可拆分；  事务跨 shard 可控。16.10 分片治理常用命令：sh.enableSharding("shop")sh.shardCollection("shop.orders", { user_id: "hashed" })sh.status()sh.isBalancerRunning()sh.startBalancer()sh.stopBalancer()生产注意：  初始导入先规划分片；  索引在所有 shard 存在；  监控 chunk 数和分布；  控制均衡窗口；  分片集合查询必须评估路由；  事务和 $lookup 成本更高。本章小结分片通过 mongos、Config Server、shard 和 balancer 将数据水平分布。它解决容量与吞吐扩展问题，但查询必须尽量带分片键，运维复杂度也明显上升。分片键设计是整个架构的核心决策。思考题  什么信号说明必须考虑分片？  哈希分片和范围分片的核心差异是什么？  为什么高频查询应包含分片键？  Config Server 保存哪些信息？  单热点文档为什么分片无法解决？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 副本集选举决定哪个成员成为 Primary。它借鉴并实现了类似 Raft 的共识思想：多数派投票、任期隔离旧主、日志进度参与选主。理解选举，才能理解故障切换时间和脑裂防护。15.1 为什么需要选举Primary 故障后必须满足：  快速选出新 Primary；  只有大多数成员同意；  拥有足够新的数据；  隔离旧 Primary；  恢复后重新加入。多数派是防脑裂的核心：3 members: 2 votes needed5 members: 3 votes needed15.2 术语            术语      说明                  term      选举任期，任期越高优先级越高              majority      多数投票成员              voting member      有投票权的成员              priority      成为主 的优先级              election timeout      触发选举的等待时间              catch-up      新主追日志，减少回退      MongoDB 实现细节与 Raft 论文并不完全相同，但目标一致。15.3 选举流程primary heartbeat lost  -&gt; secondary becomes candidate  -&gt; request votes  -&gt; majority granted  -&gt; candidate becomes primary  -&gt; clients update topology节点会考虑：  自身健康；  oplog 进度；  priority；  其他成员状态；  是否能达到多数；  心跳连通性。15.4 任期与旧主隔离old primary term 10new primary term 11旧 Primary 恢复后：  发现更高任期；  降级为 Secondary；  回滚未提交到多数的写入；  重新同步加入。这些未提交写入会形成回滚文件，需要人工检查和治理。15.5 选举触发条件常见触发：  Primary 进程退出；  Primary 网络隔离；  Primary 延迟过高；  手动 stepDown；  副本集重配置；  维护操作。主动切换：rs.stepDown(120)该命令让当前 Primary 在指定时间内不尝试重新当选。15.6 选举时间故障检测和选主需要时间，恢复窗口通常由以下因素决定：  heartbeat 间隔；  election timeout；  副本同步进度；  网络延迟；  节点负载；  多数成员健康；  DNS 或代理层恢复。不要假设切换毫秒级完成，应用需要处理连接错误和写超时。15.7 投票成员设计推荐：            拓扑      建议                  3 节点      三数据节点，跨故障域              5 节点      可容忍两节点故障              跨机房      每个机房故障不破坏多数              云上      跨可用区，不跨账号管理复杂域      避免：  双节点副本集；  多数成员在同一宿主机；  Arbiter 长期替代数据副本；  延迟节点参与关键读路径；  跨高延迟机房强求同步多数。15.8 回滚文件旧 Primary 上未同步到多数的写入可能回滚：old primary write A -&gt; not replicatednew primary commits Bold primary rejoin -&gt; rollback A处理：  查看 rollback 目录；  判断业务是否需要恢复；  通过审计或业务侧补偿；  不要盲目批量回灌；  保留复盘记录。使用 majority 写关注可降低应用确认这类写入成功的概率。15.9 客户端行为切换期间可能发生：  连接断开；  NotPrimaryNoSecondaryOK；  写超时；  重试后成功；  拓扑刷新延迟。应用建议：  使用副本集连接串；  设置服务发现超时；  幂等重试；  有限退避；  记录请求 ID；  对切换窗口告警。15.10 监控与演练指标：replica_set_membersmember_stateelections_totalelection_duration_msreplication_lag_secondsrollback_events_totalheartbeat_failure_total演练：  kill Primary；  断开 Primary 网络；  手动 stepDown；  恢复旧主；  同时关闭 Secondary；  跨可用区网络抖动。验证业务恢复时间、数据一致性和客户端重试成功率。本章小结MongoDB 用多数派投票和任期机制选择 Primary，防止分区中出现双主。选举代价是短暂不可写，以及旧主未同步多数的写入可能回滚。生产上应合理设计投票拓扑、使用 majority 写关注并常态化演练切换。思考题  为什么多数派能防止脑裂？  term 如何隔离旧 Primary？  什么写入可能进入回滚文件？  Arbiter 为什么不能替代数据副本？  故障切换期间应用应如何处理超时？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。副本集是 MongoDB 高可用的基础形态，由一个 Primary 和多个 Secondary 组成。Primary 接收写入，Secondary 同步 oplog，Primary 故障时投票节点选举新 Primary。14.1 架构Client  -&gt; Primary       | oplog       +--&gt; Secondary 1       +--&gt; Secondary 2成员类型：            成员      说明                  Primary      接收写操作              Secondary      同步数据，可提供读              Priority 0      永不成为 Primary              Hidden      对应用不可见，可做报表或备份              Delayed      延迟同步，用于误操作恢复              Arbiter      只投票不存数据      生产常用三数据节点副本集，不建议长期依赖 Arbiter 提供多数派。14.2 OplogOplog 是固定大小集合，记录可幂等重放的变更：use localdb.oplog.rs.find().sort({ $natural: -1 }).limit(3)查看大小：db.printReplicationInfo()关注：  oplog 窗口；  写入速率；  Secondary 落后时间；  大事务对 oplog 的影响；  全量同步窗口。oplog 太小可能导致 Secondary 掉队后无法增量同步。14.3 部署副本集三个节点配置：replication:  replSetName: rs0net:  bindIp: 0.0.0.0  port: 27017security:  keyFile: /etc/mongo-keyfile  authorization: enabled初始化：rs.initiate({  _id: "rs0",  members: [    { _id: 0, host: "mongo1:27017", priority: 2 },    { _id: 1, host: "mongo2:27017", priority: 1 },    { _id: 2, host: "mongo3:27017", priority: 1 }  ]})状态：rs.status()rs.conf()db.hello()14.4 复制流程client write  -&gt; primary apply  -&gt; write local oplog  -&gt; replicate to secondary  -&gt; secondary apply oplog  -&gt; ack according to write concern  -&gt; primary return success写关注：db.orders.insertOne(  { _id: "o_1", status: "CREATED" },  { writeConcern: { w: "majority", wtimeout: 3000 } })majority 确认多数节点收到写入，但应用层仍需处理超时后的状态确认。14.5 复制延迟查看：rs.printSecondaryReplicationInfo()常见原因：  Secondary 磁盘慢；  网络带宽不足；  Secondary 承担重查询；  大量写入；  大事务；  索引构建；  资源抢占；  初始同步。治理：  监控复制延迟；  控制 Secondary 负载；  延迟敏感读不走 Secondary；  避免大事务；  提升硬件和网络。14.6 Secondary 读取连接字符串：mongodb://user:pass@mongo1,mongo2,mongo3/shop?replicaSet=rs0&amp;readPreference=secondaryPreferred示例：            场景      策略                  订单写后立即读      primary              用户画像分析      secondaryPreferred              报表      hidden secondary              就近读      nearest              强一致读      primary 或 linearizable      Secondary 读带来扩展读能力，但应用必须接受复制延迟。14.7 成员维护调整优先级：cfg = rs.conf()cfg.members[0].priority = 2rs.reconfig(cfg)添加节点：rs.add("mongo4:27017")移除节点：rs.remove("mongo4:27017")维护节点时应逐步操作，确认复制状态和选举多数，避免同时下线过多投票节点。14.8 读关注常见 Read Concern：            级别      说明                  local      本节点已应用的数据              majority      多数节点已提交的数据              linearizable      强一致读，条件更多              available      分片路由场景的可用性取向      示例：db.orders.find(  { _id: "o_1" }).readConcern("majority")读关注、写关注和读偏好组合决定一致性语义。14.9 故障场景            故障      影响                  Primary 宕机      触发选举，短暂不可写              Secondary 宕机      读容量下降              两个数据节点宕机      三节点副本集失去多数              网络分区      多数侧可服务              Oplog 窗口不足      Secondary 需要全量同步              复制延迟高      Secondary 读过期      多数节点不可用时，副本集变为只读或不可服务，这是避免脑裂的代价。14.10 生产清单  至少三个数据节点；  跨机架、可用区或机房部署；  奇数投票成员；  监控复制延迟和选举；  合理配置 oplog；  关键写使用 majority；  Secondary 读有延迟预算；  备份节点不承担重负载；  变更前确认多数健康；  定期演练主节点故障。本章小结副本集通过 oplog 复制和多数派选举提供高可用。生产上必须关注投票拓扑、oplog 窗口、复制延迟、读写关注和故障域分布。Secondary 能扩展读能力，但一致性由读偏好和读关注共同决定。思考题  为什么三数据节点比两数据节点加 Arbiter 更稳？  Oplog 太小会带来什么问题？  majority 写关注是否表示所有节点都持久化？  什么业务适合 Secondary 读？  失去多数派后为什么不能继续写入？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。WiredTiger 是 MongoDB 默认存储引擎，提供文档级并发控制、MVCC 快照、B+Tree 索引、压缩和 checkpoint。理解 WiredTiger 有助于解释缓存压力、写放大、慢查询和磁盘增长。13.1 整体结构mongod  WiredTiger    cache    B+Tree collection / index    journal    checkpoint    compression写入路径：operation  -&gt; update cache  -&gt; write journal  -&gt; checkpoint flush dirty pages13.2 文档级并发WiredTiger 使用文档级并发控制。写操作默认持有文档级写意向锁，读操作使用快照。收益：  不同文档可以并行写入；  读写冲突范围小；  高并发 OLTP 表现更好。仍会阻塞的场景：            操作      冲突                  同一文档并发写      写冲突              唯一索引冲突      键冲突              多文档事务      范围和依赖冲突              DDL      元数据锁              后台索引      资源影响      应用仍应避免热点文档。13.3 Cache 与脏页WiredTiger cache 保存常用集合页和索引页。默认大小通常约为 (RAM - 1GB) / 2，具体随版本和环境变化。查看：db.serverStatus().wiredTiger.cache关注：  bytes currently in the cache；  脏页字节数；  未命中读；  淘页数量；  应用线程等待 evict；  checkpoint 时间。工作集大于缓存时，查询会更多读磁盘，延迟波动增加。13.4 JournalJournal 记录修改，用于异常崩溃后恢复未 checkpoint 的变更。写入确认：db.orders.insertOne(  { _id: "o_1", status: "CREATED" },  { writeConcern: { w: 1, j: true } })            参数      含义                  j: true      等待 journal 持久化              j: false      不显式等待              majority      多数节点确认      关键交易建议显式使用持久化确认；性能与可靠性需要结合磁盘类型评估。13.5 CheckpointCheckpoint 周期性将内存脏页落盘并生成一致点：dirty pages  -&gt; checkpoint  -&gt; stable files  -&gt; clean metadata影响：  磁盘 IO 峰值；  写延迟波动；  脏页累积；  恢复起点。大量写入、大事务或过多索引都会增加 checkpoint 压力。13.6 压缩WiredTiger 支持块压缩和索引前缀压缩，常见算法包括 snappy、zstd、zlib。具体可用算法和默认配置随版本与构建有关。查看集合：db.orders.stats()权衡：            维度      压缩更强      压缩更弱                  磁盘      更省      更耗              CPU      更多      更少              网络传输      存储层压缩不直接减少      不直接减少              恢复      时间可能更长      更短      13.7 记录与页MongoDB 文档保存在 B+Tree 页中。超大文档会带来：  页分裂；  缓存占用；  拷贝和复制成本；  更新放大；  查询网络成本。建议：  控制文档大小；  大字段拆分；  历史数据归档；  列表页投影；  避免频繁重写大数组。13.8 磁盘布局查看存储路径：db.serverCmdLineOpts()常见文件：collection-*.wtindex-*.wtJournal/WiredTigerWiredTiger.wt生产建议：  数据目录使用独立盘；  监控磁盘空间；  不手工删除 .wt 文件；  备份使用官方工具或快照；  保留足够 journal 和 checkpoint 空间。13.9 性能信号cache bytesdirty pagespages read into cachepages written from cachepages evictedtracked dirty bytescheckpoint durationtransaction conflictswrite conflicts典型判断：            信号      可能原因                  读未命中高      工作集大于 cache              脏页持续高      写入过快或 checkpoint 慢              evict waiting      cache 压力大              写冲突高      热点文档              checkpoint 长      磁盘瓶颈或脏页过多      13.10 参数与治理常见治理项：  cache size；  journal 提交间隔；  并发读写线程；  压缩算法；  checkpoint 时间；  磁盘类型；  索引数量；  文档大小；  冷热分离；  分片分散写入。参数调优必须基于指标和压测，不应直接照抄网络配置。本章小结WiredTiger 通过 MVCC、文档级并发和缓存管理支撑高并发读写。生产性能常由工作集、脏页、checkpoint、磁盘和热点文档决定。优化方向是控制文档和索引规模，让常用工作集留在内存，并避免单点热点。思考题  文档级并发是否意味着没有写冲突？  Journal 与 checkpoint 各解决什么问题？  工作集大于 cache 会表现为什么？  为什么超大文档会造成写放大？  压缩一定能提升整体性能吗？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。Change Stream 让应用可以订阅集合、数据库或整个实例的数据变更。它基于副本集 oplog，天然适合缓存失效、搜索索引同步、审计联动和事件驱动架构。12.1 基本原理application  -&gt; open change stream cursor  -&gt; tail oplog  -&gt; receive change events  -&gt; resume from resume token要求：  使用副本集或分片集群；  用户有 find 和 changeStream 权限；  oplog 窗口足够长；  应用保存 resume token；  断开后可恢复。12.2 打开 Change Stream监听集合：const stream = db.orders.watch()while (true) {  if (!stream.hasNext()) break;  printjson(stream.next())}Node.js 示例：const changeStream = db.collection("orders").watch();changeStream.on("change", async (event) =&gt; {  console.log(event);});changeStream.on("error", (err) =&gt; {  console.error(err);  changeStream.close();});12.3 事件结构插入事件示例：{  "_id": { "_data": "8266CB..." },  "operationType": "insert",  "clusterTime": { "$timestamp": { "t": 1760000000, "i": 1 } },  "fullDocument": {    "_id": "o_10001",    "status": "CREATED"  },  "ns": { "db": "shop", "coll": "orders" },  "documentKey": { "_id": "o_10001" }}常见 operationType：            类型      说明                  insert      插入              update      更新              replace      替换              delete      删除              invalidate      集合失效              drop      集合删除      12.4 过滤事件只看支付订单：const stream = db.orders.watch([  {    $match: {      "fullDocument.status": "PAID",      operationType: { $in: ["insert", "update", "replace"] }    }  }])只看特定字段更新：const stream = db.orders.watch([  {    $match: {      "updateDescription.updatedFields.status": { $exists: true }    }  }])服务端过滤能减少网络传输，但复杂业务判断通常仍在消费端完成。12.5 获取完整文档更新事件默认不包含完整最新文档：const stream = db.orders.watch([], {  fullDocument: "updateLookup"})选项：            值      行为                  default      插入替换有全文，更新只有差异              updateLookup      查询当前文档全文              whenAvailable      有缓存则返回              required      要求全文      并发更新很快时，updateLookup 看到的可能是更新后状态，需要业务幂等。12.6 Resume Token事件 _id 就是 resume token：const event = stream.next();db.tokens.updateOne(  { name: "orders-sync" },  { $set: { token: event._id, updated_at: new Date() } },  { upsert: true })恢复：const saved = db.tokens.findOne({ name: "orders-sync" });const stream = saved  ? db.orders.watch([], { resumeAfter: saved.token })  : db.orders.watch();Token 保存位置应与业务下游状态一致，避免漏事件或大量重复。12.7 起始位置从当前开始：db.orders.watch([], { startAtOperationTime: new Date() })从某个时间开始：db.orders.watch([], {  startAtOperationTime: new Date("2026-08-25T00:00:00Z")})时间起点受 oplog 窗口限制。重建下游索引时通常需要全量加增量，而不是只靠 Change Stream。12.8 事件处理模式可靠处理流程：read event  -&gt; save resume token  -&gt; apply event idempotently  -&gt; commit downstream  -&gt; advance token更稳的顺序是：  读取事件；  在下游事务中处理事件并保存新 token；  处理失败重试；  死信记录；  恢复时从已提交 token 继续。如果 token 与下游状态分开提交，可能出现重复或漏处理窗口。12.9 典型应用缓存失效：product update  -&gt; change stream  -&gt; delete redis cache  -&gt; next request rebuilds cache搜索同步：full import  -&gt; change stream  -&gt; upsert Elasticsearch document审计联动：order status change  -&gt; change stream  -&gt; emit event  -&gt; notification service12.10 容量与故障关注：  oplog 大小；  事件消费速率；  resume token 是否过期；  下游写入吞吐；  重复事件比例；  全量重建窗口。处理积压：  提高消费并行度；  批量写下游；  减少无关集合监听；  拆分消费者；  全量重建。本章小结Change Stream 是构建增量同步和事件联动的基础能力，依赖副本集 oplog 和可靠的 resume token 机制。设计时必须把 token 保存、事件幂等、全量初始化和消费积压治理纳入方案。思考题  Change Stream 为什么依赖副本集？  Resume Token 应该如何保存？  fullDocument: updateLookup 有什么并发语义？  下游索引重建时如何组合全量和增量？  Resume Token 过期怎么办？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。查询优化是把业务访问模式、索引设计、执行计划和资源限制统一起来的过程。目标不是让每条查询都极快，而是让核心链路在真实数据量和并发下稳定满足 SLA。11.1 优化流程collect slow queries  -&gt; review query semantics  -&gt; run explain executionStats  -&gt; inspect indexes and schema  -&gt; reduce scanned data  -&gt; remove blocking stages  -&gt; verify in staging  -&gt; monitor after release先确认业务真的需要该查询，再优化实现。11.2 减少返回数据只取必要字段：db.orders.find(  { user_id: "u_1001", status: "PAID" },  { order_no: 1, amount: 1, created_at: 1 }).limit(20)优化点：  使用投影；  使用分页；  控制批量大小；  大字段拆集合；  列表页避免返回详情 HTML；  图片只存引用。网络传输和应用反序列化同样是查询成本。11.3 优化过滤条件避免低选择性条件：db.orders.find({ status: { $ne: "CANCELLED" } })改为正向状态：db.orders.find({  status: { $in: ["CREATED", "WAIT_PAY", "PAID"] },  user_id: "u_1001"})原则：  等值条件放前面；  高选择性字段参与索引；  范围控制时间窗口；  避免超长 $in；  不让用户提交任意复杂查询。11.4 优化排序与分页索引：db.orders.createIndex({ user_id: 1, created_at: -1, _id: -1 })游标分页：db.orders.find({  user_id: "u_1001",  $or: [    { created_at: { $lt: lastCreatedAt } },    { created_at: lastCreatedAt, _id: { $lt: lastId } }  ]}).sort({ created_at: -1, _id: -1 }).limit(20)大 skip 会扫描并丢弃数据，深分页应改为游标或条件分页。11.5 优化聚合管道对比：// baddb.orders.aggregate([  { $group: { _id: "$user_id", total: { $sum: "$amount" } } },  { $match: { total: { $gt: 1000 } } }])改进：db.orders.aggregate([  { $match: {      status: "PAID",      created_at: { $gte: new Date("2026-08-01T00:00:00Z") }  }},  { $group: { _id: "$user_id", total: { $sum: "$amount" } } },  { $match: { total: { $gt: 1000 } } }])规则：  $match 和 $limit 前移；  $project 减少字段；  $unwind 后立即过滤；  $lookup 确保外键有索引；  避免无限 $push。11.6 索引优化重复前缀：{ user_id: 1 }{ user_id: 1, created_at: -1 }通常后者可以覆盖前者的一部分场景。删除前要确认剩余查询和写入收益。多字段查询：db.orders.find({  user_id: "u_1001",  status: "PAID",  created_at: { $gte: start, $lt: end }}).sort({ created_at: -1 })索引：db.orders.createIndex({ user_id: 1, status: 1, created_at: -1 })11.7 Schema 优化常见问题与调整：            问题      优化                  文档过大      拆分低频大字段              无界数组      拆集合并分页              高频计数      使用 $inc 或独立计数集合              列表读大文档      投影和摘要              高频 $lookup      冗余必要字段              历史数据混存      冷热分离      如果索引优化收益有限，通常需要回到数据模型。11.8 工作集优化工作集是常用数据和索引的集合。如果工作集明显大于内存，会产生更多磁盘 IO。观察：memory residentcache hit ratiodisk read / writequery latencyeviction优化：  减少索引总量；  控制文档大小；  投影减少读取；  冷数据归档；  分片分散热点；  增加内存；  分析任务移到专用系统。11.9 慢查询治理设置 Profiler：db.setProfilingLevel(1, { slowms: 100, sampleRate: 1 })查看：db.system.profile.find().sort({ ts: -1 }).limit(10)关闭：db.setProfilingLevel(0)Profiler 有额外开销，生产应控制采样率或短期开启。11.10 常见反模式            反模式      后果                  请求全量再应用过滤      数据库和网络浪费              大 skip 分页      深分页变慢              无索引排序      内存排序              复杂正则搜索      CPU 升高              索引过多      写入变慢              聚合做大数据分析      影响在线库              每次查询重建连接      延迟增加      本章小结查询优化的第一步是减少扫描和返回数据，第二步是让排序和过滤匹配索引，第三步才是调整 Schema 和资源。执行计划是判断依据，慢查询日志和监控是发现入口，预生产压测是上线保障。思考题  为什么投影也能提升性能？  大 skip 为什么慢？  $match 前移为什么重要？  如何判断工作集超过内存？  索引过多的代价是什么？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。执行计划解释 MongoDB 如何执行查询：使用哪个索引、扫描多少文档、是否内存排序、返回多少结果。优化查询不能靠猜，应以 explain 输出为准。10.1 基本用法查询计划：db.orders.find({  user_id: "u_1001",  status: "PAID"}).explain()详细执行统计：db.orders.find({  user_id: "u_1001",  status: "PAID"}).explain("executionStats")所有计划输出：db.orders.find({  user_id: "u_1001"}).explain("allPlansExecution")聚合计划：db.orders.aggregate([  { $match: { user_id: "u_1001" } },  { $sort: { created_at: -1 } }]).explain()10.2 关键字段常见字段：            字段      含义                  stage      执行阶段              winningPlan      优化器选择的计划              rejectedPlans      被拒绝计划              indexName      使用索引              keysExamined      索引条目扫描数              docsExamined      文档扫描数              nReturned      返回数              totalKeysExamined      总索引扫描              totalDocsExamined      总文档扫描              executionTimeMillis      执行时间      10.3 常见阶段            阶段      说明                  COLLSCAN      集合扫描              IXSCAN      索引扫描              FETCH      回表取文档              PROJECTION_COVERED      覆盖索引投影              SORT      内存排序              SORT_KEY_GENERATOR      排序键生成              LIMIT      限制条数              SUBPLAN      $or 子计划              GEO_NEAR      地理距离      COLLSCAN 不一定是错误，小集合或后台任务可以接受；高频在线查询通常必须避免。10.4 读取计划示例：winningPlan  FETCH    IXSCAN idx_user_time_status判断：  是否使用预期索引；  keysExamined 是否接近 nReturned；  docsExamined 是否远大于 nReturned；  是否有 SORT；  是否被覆盖索引覆盖；  执行时间是否集中在某个阶段。10.5 扫描效率理想比例：nReturned ≈ keysExamined示例：            keysExamined      docsExamined      nReturned      判断                  100      100      100      高效              100000      100000      100      选择性差              100      100000      100      过滤后置              0      1000000      0      集合扫描      索引选择性差时，调整字段顺序或查询条件比强制提示更可靠。10.6 排序计划无索引排序：SORT  FETCH    IXSCAN idx_user有索引排序：FETCH  IXSCAN idx_user_created排序阶段可能触发内存限制。分页和列表查询应尽量让排序字段进入复合索引。10.7 覆盖查询集合：db.users.insertOne({  _id: "u_1001",  name: "Alice",  level: "GOLD"})索引：db.users.createIndex({ user_id: 1, status: 1, created_at: -1 })覆盖查询需要查询字段和投影字段都在索引中。覆盖查询可以减少 FETCH，但不要为了覆盖所有场景建立过多索引。10.8 查询计划缓存MongoDB 会缓存查询计划，数据分布变化后可能重新评估。查看：db.orders.getPlanCache().list()清理：db.orders.getPlanCache().clear()不要把清理缓存当成常规优化。应优先修正索引和查询。10.9 索引提示强制使用索引：db.orders.find({  user_id: "u_1001",  status: "PAID"}).hint("idx_user_time_status")提示可以验证索引收益，但长期使用会让计划失去适应性。索引结构变化后，提示可能导致查询失败或退化。10.10 生产分析流程find slow query  -&gt; read explain  -&gt; identify scan / sort stage  -&gt; inspect indexes  -&gt; design new index  -&gt; verify executionStats  -&gt; deploy during low peak  -&gt; monitor latency and write cost上线新索引时要注意后台构建策略、副本集影响和磁盘空间。10.11 常见计划问题            现象      可能原因                  COLLSCAN      无索引或条件不满足前缀              大量 docsExamined      索引选择性差              SORT      排序字段不在索引              rejectedPlans 多      索引重复或冲突              计划变化      数据分布或缓存变化              聚合慢      $match 后置或数据放大      本章小结执行计划把查询性能问题变成可观察的证据。重点看 winningPlan、扫描数量、返回数量和排序阶段。查询优化应先减少输入数据，再优化索引顺序和排序，最后考虑缓存与提示。思考题  COLLSCAN 一定有问题吗？  keysExamined 和 nReturned 的关系说明什么？  为什么 SORT 阶段需要关注内存？  覆盖索引的代价是什么？  为什么不建议长期使用 hint？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 内置文本索引和地理空间索引，适合商品搜索、门店查询、附近服务和位置筛选。它们降低了引入搜索引擎的初始成本，但复杂相关性排序和大规模搜索仍可能需要 Elasticsearch 等专用系统。9.1 文本索引创建索引：db.products.createIndex(  { title: "text", description: "text", tags: "text" },  { name: "idx_product_text" })查询：db.products.find({  $text: { $search: "轻薄 笔记本" }})返回相关性分数：db.products.find(  { $text: { $search: "轻薄 笔记本" } },  { score: { $meta: "textScore" } }).sort({ score: { $meta: "textScore" } })适合：  中小规模全文检索；  后台管理搜索；  多字段关键词匹配；  不需要复杂分词和排序的系统。9.2 文本索引限制            限制      说明                  分词能力      内置分词对中文等语言不一定满足业务              一个集合      通常只能有一个文本索引              复杂排序      相关性以外的复杂排序受限              高级搜索      拼写纠错、同义词、权重调优不如搜索引擎              写入成本      文本索引较大      中文搜索常见方案：  应用层分词后保存关键字数组；  使用 ngram 分词；  使用 Elasticsearch；  使用云搜索服务。9.3 关键词数组方案应用分词后写入：db.products.insertOne({  title: "轻薄笔记本电脑",  keywords: ["轻薄", "笔记本", "电脑", "notebook"]})索引：db.products.createIndex({ keywords: 1 })查询：db.products.find({  keywords: { $all: ["轻薄", "笔记本"] }})优点是查询简单、索引可控；缺点是分词、同义词和相关性行需要应用维护。9.4 地理数据类型GeoJSON 点：{  name: "上海门店",  location: {    type: "Point",    coordinates: [121.4737, 31.2304]  }}经纬度顺序是 [longitude, latitude]，不要写反。传统坐标对：{  name: "北京门店",  loc: [116.4074, 39.9042]}新项目建议使用 GeoJSON。9.5 2dsphere 索引创建：db.stores.createIndex({ location: "2dsphere" })插入：db.stores.insertOne({  name: "南京西路店",  city: "上海",  location: {    type: "Point",    coordinates: [121.4512, 31.2294]  }})附近查询：db.stores.find({  location: {    $near: {      $geometry: {        type: "Point",        coordinates: [121.4737, 31.2304]      },      $maxDistance: 3000,      $minDistance: 0    }  }})单位是米，适合球面距离计算。9.6 地理范围查询查询圆形范围：db.stores.find({  location: {    $geoWithin: {      $centerSphere: [        [121.4737, 31.2304],        3 / 6378.1      ]    }  }})查询矩形范围：db.stores.find({  location: {    $geoWithin: {      $box: [        [121.3, 31.1],        [121.6, 31.4]      ]    }  }})$geoWithin 不返回距离，也不排序；需要排序时使用 $near 或聚合 $geoNear。9.7 $geoNear 聚合db.stores.aggregate([  {    $geoNear: {      near: {        type: "Point",        coordinates: [121.4737, 31.2304]      },      distanceField: "distance",      maxDistance: 5000,      query: { city: "上海" },      spherical: true    }  },  { $limit: 10 }])要求：  必须有地理索引；  通常是第一个阶段；  每个管道只能有一个 $geoNear；  输出距离字段便于展示；  可以附加普通条件。9.8 地理查询性能优化建议：  必须创建 2dsphere 或 2d 索引；  限制返回条数；  附加城市或状态条件缩小范围；  避免全球范围查询；  高并发位置查询增加缓存；  大规模地理计算考虑专用地理服务。9.9 混合搜索设计门店搜索示例：keyword search  -&gt; candidate IDs  -&gt; geo filter  -&gt; business filter  -&gt; ranking文档：{  store_id: "s_100",  name: "门店名称",  keywords: ["coffee", "coffee shop"],  city: "上海",  score: 98,  location: { type: "Point", coordinates: [121.47, 31.23] }}索引：db.stores.createIndex({ city: 1, keywords: 1, score: -1 })db.stores.createIndex({ location: "2dsphere" })复杂相关性和地理位置混合排序可以由搜索服务完成，MongoDB 保存主数据。9.10 常见问题            问题      排查                  中文搜索效果差      分词能力不足              文本索引不生效      未创建或查询字段不匹配              地理结果异常      经纬度顺序错误              $near 很慢      无地理索引或范围过大              $geoNear 报错      不在第一阶段或无索引              搜索写入变慢      索引过大      本章小结文本和地理索引让 MongoDB 能覆盖中小型搜索和位置查询场景。文本搜索重点是分词和相关性行，地理搜索重点是数据格式、索引和距离排序。业务复杂后，可以将 MongoDB 作为主存储，把搜索能力交给专用系统。思考题  文本索引对中文搜索的主要限制是什么？  GeoJSON 坐标顺序是什么？  $near 和 $geoWithin 的区别是什么？  $geoNear 为什么通常放在第一阶段？  什么时候应该引入 Elasticsearch？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。聚合管道把文档流经过一组阶段处理，每个阶段接收上一步输出并生成下一步输入。它适合统计、分组、变形、关联和多集合组装，但大数据量时必须优先匹配和索引，必要时使用允许落盘的排序。8.1 管道思想collection  -&gt; $match  -&gt; $project  -&gt; $group  -&gt; $sort  -&gt; $limit每个阶段都在内存中构造新文档流，因此阶段顺序和过滤位置直接决定性能。8.2 准备数据db.orders.insertMany([  { order_no: "O1", user_id: "u1", status: "PAID", amount: NumberDecimal("200.00"), items: [{ sku: "A", qty: 2 }, { sku: "B", qty: 1 }] },  { order_no: "O2", user_id: "u1", status: "PAID", amount: NumberDecimal("300.00"), items: [{ sku: "A", qty: 1 }] },  { order_no: "O3", user_id: "u2", status: "CREATED", amount: NumberDecimal("100.00"), items: [{ sku: "C", qty: 5 }] }])8.3 $match 与 $project先过滤：db.orders.aggregate([  { $match: {      status: "PAID",      created_at: { $gte: new Date("2026-08-01T00:00:00Z") }  }}])再投影：db.orders.aggregate([  { $match: { status: "PAID" } },  { $project: {      _id: 0,      order_no: 1,      user_id: 1,      amount: 1  }}])$match 应尽量放在管道前面，才能使用索引并减少后续数据量。8.4 $group按用户统计：db.orders.aggregate([  { $match: { status: "PAID" } },  { $group: {      _id: "$user_id",      order_count: { $sum: 1 },      total_amount: { $sum: "$amount" },      max_amount: { $max: "$amount" },      last_order_no: { $last: "$order_no" }  }},  { $sort: { total_amount: -1 } },  { $limit: 10 }])常见累加器：            累加器      说明                  $sum      求和              $avg      平均              $min / $max      极值              $push      收集值到数组              $addToSet      收集去重值              $first / $last      首尾值      $push 可能形成大数组，要限制分组规模。8.5 $unwind展开订单明细：db.orders.aggregate([  { $match: { status: "PAID" } },  { $unwind: "$items" },  { $group: {      _id: "$items.sku",      quantity: { $sum: "$items.qty" },      order_count: { $sum: 1 }  }},  { $sort: { quantity: -1 } }])空数组处理：{ $unwind: { path: "$items", preserveNullAndEmptyArrays: true } }$unwind 会放大文档数量，放在 $match 之后能减少处理量。8.6 $lookup关联用户：db.orders.aggregate([  { $match: { status: "PAID" } },  { $lookup: {      from: "users",      localField: "user_id",      foreignField: "_id",      as: "user"  }},  { $unwind: "$user" },  { $project: {      order_no: 1,      amount: 1,      user_name: "$user.name",      user_level: "$user.level"  }}])关联集合应确保外键有索引。高频 $lookup 通常说明模型冗余或拆分需要调整。8.7 $facet一次返回列表和统计：db.products.aggregate([  { $match: { status: "ON_SALE" } },  { $facet: {      page: [        { $sort: { created_at: -1 } },        { $skip: 0 },        { $limit: 20 }      ],      summary: [        { $group: {            _id: null,            total: { $sum: 1 },            avg_price: { $avg: "$price" }        }}      ]  }}])$facet 会共享前置阶段输出，但仍要控制数据规模。8.8 窗口函数按用户计算订单时间序：db.orders.aggregate([  { $match: { status: "PAID" } },  { $sort: { user_id: 1, created_at: 1 } },  { $setWindowFields: {      partitionBy: "$user_id",      sortBy: { created_at: 1 },      output: {        user_order_seq: { $documentNumber: {} },        rolling_amount: {          $sum: "$amount",          window: { documents: [-2, 0] }        }      }  }}])窗口算子适合报表和排名，复杂窗口仍应评估是否由分析引擎承担。8.9 分页与排序基础分页：db.orders.aggregate([  { $match: { user_id: "u1" } },  { $sort: { created_at: -1 } },  { $skip: 0 },  { $limit: 20 }])性能建议：  $match 放前；  $limit 尽量提前；  排序字段进索引；  避免大 $skip；  用游标条件分页；  只投影必要字段。8.10 内存限制某些阶段有内存限制。排序可以显式允许落盘：db.orders.aggregate(  [    { $sort: { created_at: -1 } }  ],  { allowDiskUse: true })注意：  落盘会更慢；  占用临时空间；  可能影响其他请求；  应先优化索引和过滤；  大分析任务考虑 ClickHouse 等分析系统。8.11 输出到集合保存结果：db.orders.aggregate([  { $match: { status: "PAID" } },  { $group: {      _id: "$user_id",      total_amount: { $sum: "$amount" }  }},  { $merge: {      into: "user_order_stats",      on: "_id",      whenMatched: "replace",      whenNotMatched: "insert"  }}])$out 覆盖输出集合，$merge 支持合并策略。定时任务需要幂等和调度审计。8.12 常见问题            问题      排查                  聚合很慢      $match 太晚、无索引、数据放大              内存超限      分组或排序过大              $lookup 慢      外键无索引              $unwind 后数量暴涨      数组过大              结果金额不准      使用 double 而非 Decimal128              随机不一致      $first / $last 前未排序      本章小结聚合管道是 MongoDB 的数据处理流水线。写管道时坚持先过滤、再投影、再分组或关联，排序和分页尽量依靠索引。小型实时统计适合管道，大规模分析应考虑专用分析引擎。思考题  为什么 $match 应放在管道前面？  $unwind 为什么会放大数据量？  $lookup 的外键为什么需要索引？  $out 与 $merge 有什么区别？  allowDiskUse 能解决所有性能问题吗？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。查询操作符决定筛选、范围、数组、类型和模式匹配能力。操作符越强，越要注意语义、索引支持和性能边界。写查询时，应先明确返回结果，再验证执行计划。7.1 比较操作符            操作符      说明                  $eq      等于              $ne      不等于              $gt      大于              $gte      大于等于              $lt      小于              $lte      小于等于              $in      在集合中              $nin      不在集合中      等值查询：db.orders.find({ status: "PAID" })等价显式写法：db.orders.find({ status: { $eq: "PAID" } })范围查询：db.orders.find({  created_at: {    $gte: new Date("2026-08-01T00:00:00Z"),    $lt: new Date("2026-09-01T00:00:00Z")  }})$in 适合少量枚举值：db.orders.find({  status: { $in: ["CREATED", "WAIT_PAY", "PAID"] }})7.2 逻辑操作符同一对象中的多个条件是 AND：db.orders.find({  user_id: "u_1001",  status: "PAID"})显式 $and：db.orders.find({  $and: [    { user_id: "u_1001" },    { amount: { $gte: NumberDecimal("100.00") } }  ]})OR：db.orders.find({  $or: [    { status: "CLOSED" },    { paid_at: { $lt: new Date("2026-08-01T00:00:00Z") } }  ]})NOT：db.orders.find({  status: { $ne: "CANCELLED" }})$ne 和 $nin 的选择性和索引利用通常不如正向等值条件。7.3 存在与类型字段存在：db.products.find({ color: { $exists: true } })字段不存在：db.products.find({ color: { $exists: false } })类型判断：db.products.find({ price: { $type: "decimal" } })常用于数据质量排查。生产写入应有应用对象和 validator 统一类型，避免同一字段混用字符串和数字。7.4 数组查询数据：{  sku_id: "sku_10001",  tags: ["hot", "new", "office"],  attrs: [    { name: "color", value: "black" },    { name: "size", value: "M" }  ]}包含一个元素：db.products.find({ tags: "hot" })包含全部元素：db.products.find({ tags: { $all: ["hot", "new"] } })数组长度：db.products.find({ tags: { $size: 3 } })$size 不能直接使用索引，也不支持范围；若需要按长度查询，建议冗余计数字段。7.5 数组元素匹配必须同时满足同一子文档中的多个条件：db.products.find({  attrs: {    $elemMatch: {      name: "color",      value: "black"    }  }})普通写法：db.products.find({  "attrs.name": "color",  "attrs.value": "black"})普通写法可能匹配到不同子文档的组合，语义与 $elemMatch 不同。7.6 正则查询前缀匹配：db.users.find({ email: /^alice@example\.com$/ })大小写不敏感：db.users.find({ name: /^alice$/i })非前缀正则：db.users.find({ name: /alice/ })索引支持：  区分大小写的前缀匹配可以利用索引；  大小写不敏感或非前缀匹配通常扫描更多数据；  高频搜索应使用文本索引或搜索引擎；  复杂正则会显著消耗 CPU。7.7 模糊匹配替代方案            需求      建议                  固定前缀      前缀正则或范围查询              包含关键词      text index              中文搜索      ngram 或 Elasticsearch              自动补全      前缀字段或搜索服务              多条件搜索      搜索引擎      业务搜索不要把 MongoDB 正则当成通用搜索引擎。7.8 投影与分页只取必要字段：db.orders.find(  { user_id: "u_1001" },  { order_no: 1, status: 1, amount: 1, created_at: 1 })游标分页：const lastCreatedAt = new Date("2026-08-25T10:00:00Z");const lastId = ObjectId("66cb1f0b8d0f4e51f0a8b123");db.orders.find({  user_id: "u_1001",  $or: [    { created_at: { $lt: lastCreatedAt } },    { created_at: lastCreatedAt, _id: { $lt: lastId } }  ]}).sort({ created_at: -1, _id: -1 }).limit(20)排序键应唯一，否则同一页边界可能重复或漏数据。7.9 查询语义陷阱            写法      语义                  { field: null }      字段值为 null 或字段不存在              { field: { $exists: false } }      字段不存在              { array: value }      数组包含 value              { array: { $all: [...] } }      包含所有指定元素              $elemMatch      同一数组元素满足条件              {}      全部文档      字段缺失和 null 在业务上是否等价，必须在 Schema 契约中明确。7.10 性能边界需要谨慎的操作：  $nin；  $ne；  非前缀正则；  $where；  大 $in；  无索引排序；  无投影大文档读取；  集合扫描统计。$where 可以执行 JavaScript，生产应避免使用，除非有严格控制。7.11 查询安全应用层应使用驱动提供的参数化查询结构，而不是拼接字符串。同时：  限制用户可查询字段；  限制返回条数；  禁止用户输入任意操作符；  敏感字段脱敏；  查询超时可控；  为高频查询建索引。本章小结查询操作符的重点是语义正确和索引友好。等值和范围优先，数组和正则要理解匹配粒度，分页要使用唯一排序键。复杂搜索和统计应交给合适的索引或搜索引擎，而不是让 MongoDB 承担所有模糊查询。思考题  为什么 $ne 和 $nin 通常性能不好？  $elemMatch 和普通数组条件有什么区别？  哪些正则可以利用索引？  null 与字段缺失有什么差异？  游标分页为什么需要唯一排序键？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。索引决定查询能否以可控代价执行。MongoDB 支持单字段、复合、多键、文本、地理、哈希、部分、稀疏、TTL 和唯一索引。设计索引时，先看查询条件和排序，再看写入成本和内存占用。6.1 没有索引会发生什么集合扫描：query condition  -&gt; scan collection documents  -&gt; filter one by one  -&gt; return matches数据量小可以接受，数据量大后会导致：  查询延迟高；  CPU 和 IO 增加；  工作集被冷数据污染；  阻塞其他请求；  复制延迟增加。6.2 单字段索引创建：db.orders.createIndex({ user_id: 1 })1 表示升序，-1 表示降序。单字段索引支持正反排序。查询：db.orders.find({ user_id: "u_1001" }).sort({ created_at: -1 })该查询若没有复合索引，user_id 索引命中后仍需要内存排序。6.3 复合索引创建：db.orders.createIndex(  { user_id: 1, created_at: -1, status: 1 },  { name: "idx_user_time_status"})适用查询：db.orders.find({ user_id: "u_1001" })db.orders.find({ user_id: "u_1001", status: "PAID" })db.orders.find({ user_id: "u_1001" })  .sort({ created_at: -1 })不适用：db.orders.find({ status: "PAID" })复合索引遵循最左前缀原则，字段顺序应结合等值条件、范围条件和排序。6.4 ESR 原则推荐顺序：Equality -&gt; Sort -&gt; Range示例查询：db.orders.find({  user_id: "u_1001",  created_at: { $gte: start, $lt: end }}).sort({ status: 1, created_at: -1 })索引建议：db.orders.createIndex({  user_id: 1,  status: 1,  created_at: -1})先放等值字段，再放排序字段，最后放范围字段，可以减少扫描和内存排序。6.5 多键索引数组字段自动使用多键索引：db.products.createIndex({ tags: 1 })查询：db.products.find({ tags: "hot" })限制：  一个复合索引中通常只能有一个多键字段；  数组过大索引开销高；  无界数组会放大写入成本；  索引条目数量需要关注。6.6 唯一索引业务单号：db.orders.createIndex(  { order_no: 1 },  { unique: true })用户名：db.users.createIndex(  { email: 1 },  { unique: true, sparse: true })唯一索引是数据库层幂等和约束的重要手段。应用层校验不能替代唯一约束。6.7 部分索引只索引活跃订单：db.orders.createIndex(  { user_id: 1, created_at: -1 },  {    name: "idx_active_user_time",    partialFilterExpression: {      status: { $in: ["CREATED", "WAIT_PAY", "PAID"] }    }  })适合：  查询永远限定子集；  历史数据量大；  希望降低索引大小；  写入热点明显。查询条件必须覆盖部分索引条件，否则优化器不能使用。6.8 TTL 索引会话过期：db.sessions.createIndex(  { expires_at: 1 },  { expireAfterSeconds: 0 })日志固定保留：db.login_events.createIndex(  { created_at: 1 },  { expireAfterSeconds: 60 * 60 * 24 * 30 })注意：  TTL 后台线程周期性删除，不是精确到秒；  只能用于 Date 或包含 Date 的数组；  不适合替代业务级清理审计；  删除大量数据仍会影响性能；  需要监控删除速率。6.9 索引查看与删除查看：db.orders.getIndexes()查看大小：db.orders.stats({ indexDetails: true })删除：db.orders.dropIndex("idx_user_time_status")删除前必须确认没有高频查询依赖该索引，并保留回滚脚本。6.10 隐藏索引MongoDB 支持隐藏索引用于验证影响：db.orders.hideIndex("idx_user_time_status")恢复：db.orders.unhideIndex("idx_user_time_status")隐藏索引仍占存储，但优化器不使用，适合删除前观察。6.11 索引开销每个索引都会带来：  写入放大；  磁盘占用；  内存占用；  checkpoint 压力；  维护成本。索引数量控制建议：  每个索引都有明确查询；  定期合并重复前缀索引；  删除无效索引；  用执行计划验证效果；  在预生产环境压测。6.12 常见问题            问题      原因                  查询没有走索引      条件不带最左前缀              排序内存高      排序字段不在索引中              写入变慢      索引过多              唯一冲突      历史重复或空值策略              部分索引未使用      查询条件不满足              TTL 不准时      后台删除机制      本章小结索引设计遵循 ESR 原则，等值、排序、范围字段顺序要匹配访问模式。唯一索引用于数据库层约束，TTL、部分索引、隐藏索引是治理成本和变更风险的重要工具。索引不是越多越好，每个索引都必须被查询和监控证明值得存在。思考题  复合索引为什么要遵循最左前缀？  ESR 原则如何减少内存排序？  部分索引为什么能降低成本？  TTL 索引适合什么场景？  删除索引前为什么要隐藏验证？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB Schema 设计不是先画实体关系图，而是先列访问模式：应用如何读、如何写、如何分页、如何统计、是否需要扩展。文档边界通常由读取边界、事务边界和分片边界共同决定。5.1 设计流程identify entities  -&gt; list queries and writes  -&gt; choose document boundary  -&gt; design indexes  -&gt; evaluate growth  -&gt; review transaction and sharding  -&gt; validate with real volume每个核心用例应包含：            项目      示例                  查询条件      user_id + created_at              返回字段      订单号、状态、金额              排序      created_at desc              频率      高              写入路径      创建、支付、发货              数据增长      订单明细有限              一致性要求      状态强一致      5.2 内嵌模式适合用户资料中的地址：{  _id: "u_1001",  name: "Alice",  default_address_id: "addr_1",  addresses: [    { _id: "addr_1", city: "Shanghai", street: "南京西路 100 号" },    { _id: "addr_2", city: "Beijing", street: "朝阳路 8 号" }  ]}适用：  数量有限；  与主文档一起读取；  更新频率低；  不需要独立查询和分页；  生命周期一致。5.3 引用模式订单明细若可能很多，应独立建模：db.order_items.insertOne({  order_id: "o_10001",  sku_id: "sku_10001",  quantity: 2,  price: NumberDecimal("199.00")})适用：  子文档无界增长；  需要独立索引和查询；  不同方频繁修改；  数据量大到影响主文档；  生命周期不同。5.4 混合模式订单主表保存摘要，明细独立保存：{  _id: "o_10001",  user_id: "u_1001",  status: "PAID",  item_count: 3,  total_amount: NumberDecimal("597.00"),  created_at: new Date()}列表页只读主表，详情页再读明细。这样能控制文档大小和网络传输。5.5 一对多设计            场景      建议                  用户与地址      内嵌              文章与少量标签      内嵌数组              用户与订单      引用              订单与明细      摘要内嵌或拆分              商品与评价      独立集合并分页              系统与日志      独立集合，按时间分片      不要只看关系基数，还要看读取方式和增长规模。5.6 多对多设计商品与分类：{  _id: "sku_10001",  title: "轻薄笔记本",  category_ids: ["c_notebook", "c_work"]}适合商品侧按分类过滤。若还需要从分类查商品数量，可以增加冗余计数或单独维护映射集合：{  category_id: "c_notebook",  sku_id: "sku_10001"}多对多通常需要在查询方向、冗余度和一致性之间取舍。5.7 反范式与冗余订单明细中冗余商品快照：{  order_id: "o_10001",  sku_id: "sku_10001",  sku_title: "轻薄笔记本",  price: NumberDecimal("5999.00")}订单必须保存成交时价格和标题快照，不能每次关联最新商品数据。对可接受略旧的数据，可以冗余但需要更新策略和一致性说明。冗余原则：  快照型数据适合冗余；  高频变化数据谨慎冗余；  必须明确谁负责更新；  必须有对账或补偿；  避免多层嵌套冗余。5.8 状态机设计状态流转字段：{  _id: "o_10001",  status: "WAIT_PAY",  version: 3,  status_history: [    { status: "CREATED", at: new Date() },    { status: "RISK_PASSED", at: new Date() }  ]}条件更新：db.orders.updateOne(  { _id: "o_10001", status: "WAIT_PAY", version: 3 },  {    $set: { status: "PAID" },    $inc: { version: 1 },    $push: { status_history: { status: "PAID", at: new Date() } }  })状态机条件能防止旧请求、重复回调和并发更新覆盖。5.9 时间与时区统一规范：  数据库保存 UTC Date；  展示层转本地时区；  API 可以同时输出业务时区字符串；  避免字符串日期；  范围查询统一 Date 类型；  审计字段包括 created_at 和 updated_at。5.10 大字段处理商品详情 HTML 可能在几百 KB 到几 MB：products       保存列表字段和摘要product_detail 保存大字段object storage 保存图片和附件好处：  列表查询更快；  工作集更稳定；  更新详情不影响主文档；  大内容可独立缓存；  备份和迁移更灵活。5.11 集合治理集合命名建议：usersordersorder_itemsproduct_reviewsaudit_login_events治理要求：  一个集合职责清晰；  不要无限创建按用户或按月份的动态集合；  TTL 集合单独规划；  审计集合与业务集合隔离；  索引数量受控；  废弃集合及时归档下线。5.12 设计评审清单  前十个查询是否有索引；  是否存在无界数组；  文档大小是否可控；  常见更新是否原子；  是否需要多文档事务；  金额和时间的类型是否正确；  列表页是否只取必要字段；  是否有分片键候选；  是否有冷热拆分；  是否有数据保留和归档策略；  是否有 validator；  是否压测过真实容量。本章小结MongoDB Schema 设计围绕访问模式展开：一起读的数据可以内嵌，无界增长的数据要拆分，跨实体事实可以冗余快照，状态变更用条件更新和版本控制。设计完成后，用查询、写入、增长、事务和分片五个维度反复评审。思考题  什么时候必须把内嵌数组拆成独立集合？  订单为什么应保存商品价格快照？  状态机条件更新解决什么并发问题？  大字段为什么不应放在主文档？  如何判断一个 Schema 是否支持未来分片？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。CRUD 是 MongoDB 最常用的操作。核心原则是：写操作明确 _id 和更新策略，读操作明确投影、排序和分页，批量操作控制并发，所有高频查询都有索引支撑。4.1 插入文档插入单条：db.orders.insertOne({  _id: "o_202608250001",  user_id: "u_1001",  status: "CREATED",  amount: NumberDecimal("199.00"),  items: [    { sku_id: "sku_10001", quantity: 1, price: NumberDecimal("199.00") }  ],  created_at: new Date()})插入多条：db.products.insertMany([  { sku_id: "sku_10001", title: "键盘", price: NumberDecimal("399.00") },  { sku_id: "sku_10002", title: "鼠标", price: NumberDecimal("199.00") }], { ordered: false })ordered: false 允许继续处理后续文档，但应用仍需处理重复键等错误。4.2 查询文档查询单条：db.orders.findOne({ _id: "o_202608250001" })查询多条：db.orders.find({  user_id: "u_1001",  status: { $in: ["CREATED", "PAID"] }})指定字段：db.orders.find(  { user_id: "u_1001" },  { order_no: 1, status: 1, amount: 1, created_at: 1, _id: 0 })排序和限制：db.orders.find({ user_id: "u_1001" })  .sort({ created_at: -1 })  .skip(0)  .limit(20)大分页不建议使用大 skip，应使用范围条件游标分页。4.3 更新文档更新单条：db.orders.updateOne(  { _id: "o_202608250001", status: "CREATED" },  {    $set: { status: "PAID", paid_at: new Date() },    $currentDate: { updated_at: true }  })更新多条：db.products.updateMany(  { status: "ON_SALE" },  { $set: { channel: "online" } })常用更新操作符：            操作符      说明                  $set      设置字段              $unset      删除字段              $inc      数值递增              $mul      数值相乘              $push      数组添加              $pull      数组删除              $addToSet      去重添加              $currentDate      更新时间      不要用不带更新操作符的文档整体替换，除非确实要覆盖。4.4 数组更新添加标签：db.products.updateOne(  { sku_id: "sku_10001" },  { $addToSet: { tags: "hot" } })移除标签：db.products.updateOne(  { sku_id: "sku_10001" },  { $pull: { tags: "cold" } })更新数组中匹配元素：db.orders.updateOne(  {    _id: "o_202608250001",    "items.sku_id": "sku_10001"  },  {    $set: { "items.$.quantity": 2 }  })数组位置更新要确认匹配条件唯一，否则可能更新到非预期元素。4.5 删除文档删除单条：db.orders.deleteOne({ _id: "o_202608250001" })删除多条：db.orders.deleteMany({  status: "CLOSED",  created_at: { $lt: new Date("2026-01-01T00:00:00Z") }})删除建议：  生产删除前先查询确认数量；  保留审计数据时使用软删除；  大量删除分批执行；  删除条件必须有索引；  删除不可轻易回滚。4.6 批量写入db.orders.bulkWrite([  { insertOne: { document: { _id: "o_1", status: "CREATED" } } },  { updateOne: {      filter: { _id: "o_1", status: "CREATED" },      update: { $set: { status: "PAID" } }  }},  { deleteOne: { filter: { _id: "o_2", status: "CANCELLED" } } }], { ordered: false })批量写入可以减少网络往返，但要控制批次大小，并处理每类错误。4.7 查找并修改const result = db.orders.findOneAndUpdate(  { _id: "o_202608250001", status: "CREATED" },  { $set: { status: "PROCESSING" } },  {    returnDocument: "after",    projection: { status: 1 },    sort: { created_at: 1 }  })适用：  原子领取任务；  状态机流转；  计数器更新；  避免读改写竞争。类似命令还有 findOneAndReplace、findOneAndDelete。4.8 乐观并发控制文档版本字段：db.orders.updateOne(  { _id: "o_202608250001", version: 3 },  {    $set: { status: "PAID" },    $inc: { version: 1 }  })若 matchedCount 为 0，说明版本变化或状态不满足条件，应重新读取业务对象。4.9 写关注插入：db.orders.insertOne(  { _id: "o_202608250003", status: "CREATED" },  { writeConcern: { w: "majority", j: true, wtimeout: 3000 } })            参数      说明                  w      等待多少节点确认              j      是否等待 journal              wtimeout      等待超时      重要交易数据通常使用 majority 写关注；日志类数据可以根据成本选择较低确认级别。4.10 常见错误            问题      原因                  Duplicate key      _id 或唯一索引重复              Document validation failure      validator 拒绝              Query exceeded memory      聚合或排序无索引              Update matched 0      条件不匹配或版本变化              Modified 0      值已相同              WriteConcernTimeout      副本确认超时              Not primary      写入 non-primary      本章小结MongoDB CRUD 的关键是把“条件更新”作为默认思维方式，用状态机条件、版本号和原子更新避免读改写竞争。读操作应始终控制投影、排序和数量；写操作要明确写关注、批量边界和错误处理。思考题  updateOne 和整体替换有什么区别？  为什么大分页不推荐大 skip？  findOneAndUpdate 适合什么场景？  版本号如何实现乐观锁？  majority 写关注解决什么问题？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 的基本数据单位是 BSON 文档，文档组成集合，集合组成数据库。文档模型的价值不只是“字段灵活”，而是让一组经常一起读取的数据可以放在一个边界内，从而减少 JOIN 和多次查询。3.1 数据层级Database  Collection    Document      Field示例：use shopdb.products.insertOne({  sku_id: "sku_10001",  title: "轻薄笔记本",  price: NumberDecimal("5999.00"),  attrs: {    cpu: "16核",    memory: "32GB"  },  tags: ["轻薄", "高续航"],  status: "ON_SALE",  created_at: new Date()})3.2 _id 与 ObjectId每个文档必须有 _id，集合内唯一。若插入时没有提供，MongoDB 默认生成 ObjectId：{  _id: ObjectId("66cb1f0b8d0f4e51f0a8b123")}ObjectId 通常包含时间戳信息，但不应把它当成业务时间，业务仍应显式保存 created_at。业务也可以使用自然键：db.orders.insertOne({  _id: "o_20260825_000001",  status: "CREATED"})3.3 常用 BSON 类型            类型      示例                  String      "Alice"              Int32 / Int64      NumberLong("100")              Double      1.5              Decimal128      NumberDecimal("19.90")              Boolean      true              Date      new Date()              ObjectId      ObjectId()              Array      ["a", "b"]              Object      { city: "Shanghai" }              Null      null      金额使用 Decimal128，计数使用整数，时间使用 Date。不要用字符串保存时间和数字。3.4 字段名设计字段名会保存在每个文档中，过长字段名会增加存储和网络成本。但不应为了省空间使用不可读的缩写。推荐：{  user_id: "u_1001",  order_no: "O202608250001",  total_amount: NumberDecimal("199.00"),  created_at: new Date()}避免：{  uid: "u_1001",  ono: "O202608250001",  amt: 199}团队应统一命名规范，例如 snake_case 或 camelCase，并在应用对象映射层保持一致。3.5 嵌套文档适合表示从属关系：{  order_no: "O202608250001",  status: "PAID",  shipping_address: {    country: "中国",    city: "上海",    street: "南京西路 100 号"  },  items: [    { sku_id: "sku_10001", quantity: 1 }  ]}优点：  一次读取完整订单；  地址与订单生命周期一致；  单文档事务保证；  避免频繁 join。限制：  无界增长；  高频局部更新；  超大文档；  多方共享修改。3.6 数组建模数组可以表达标签、明细、权限等：{  article_id: "a_100",  title: "MongoDB 设计",  tags: ["mongodb", "database"],  comments_count: 120}数组适合有限且随主文档读取的数据。订单明细若持续增长，需要评估拆分；评论量大时通常拆成独立集合并分页查询。3.7 引用建模用户与订单示例：db.users.insertOne({  _id: "u_1001",  name: "Alice",  level: "GOLD"})db.orders.insertOne({  _id: "o_10001",  user_id: "u_1001",  amount: NumberDecimal("299.00"),  status: "PAID"})查询订单并关联用户：db.orders.aggregate([  { $match: { _id: "o_10001" } },  { $lookup: {      from: "users",      localField: "user_id",      foreignField: "_id",      as: "user"  }},  { $unwind: "$user" }])$lookup 方便，但高频关联查询通常说明模型需要调整或使用冗余字段。3.8 Schema 灵活性同一集合中的文档可以有不同字段：db.products.insertOne({ sku_id: "A", title: "A" })db.products.insertOne({ sku_id: "B", title: "B", color: "red" })这不是“没有 Schema”，而是 Schema 由应用和 validator 约束。集合级验证示例：db.runCommand({  collMod: "products",  validator: {    $jsonSchema: {      required: ["sku_id", "title"],      properties: {        sku_id: { bsonType: "string", minLength: 1 },        price: { bsonType: "decimal", minimum: 0 }      }    }  },  validationLevel: "strict",  validationAction: "error"})3.9 文档大小与工作集单个 BSON 文档有大小上限，历史上常见为 16MB。生产设计应让常用文档远小于该值。过大文档的问题：  读写放大；  内存压力大；  更新冲突；  复制延迟增加；  网络传输浪费。处理方式：  拆分历史明细；  大文本或图片放对象存储；  低频字段独立集合；  列表分页；  使用覆盖常用字段的投影。3.10 建模原则  从查询和更新模式出发；  一起读的数据放一起；  避免无界数组；  高频更新计数可单独集合；  金额、时间、ID 类型要明确；  常用查询要有索引支撑；  事务边界尽量小；  分片可能性要提前评估；  集合职责清晰，不做万能大杂烩；  应用契约与 validator 双重约束。本章小结文档模型通过嵌套和数组减少不必要的关联，让数据形态接近业务读取方式。但灵活不等于随意，必须控制文档大小、数组增长、字段类型和更新频率。好的 MongoDB 设计通常先列出查询和写入模式，再决定内嵌、引用或混合建模。思考题  为什么金额应使用 Decimal128？  无界数组会带来哪些风险？  什么时候应该拆分集合？  $lookup 高频出现说明什么？  validator 能替代应用层校验吗？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。本章搭建一个可用于学习和验证的 MongoDB 环境。学习环境可以用 Docker 或单机二进制包，生产环境必须部署副本集，并提前规划认证、磁盘、监控和备份。2.1 版本选择建议选择当前仍在维护生命周期内的稳定版本，例如 MongoDB 7.x 或 8.x。不同版本的默认参数、审计能力、聚合算子和分片策略可能有差异。生产升级前应阅读目标版本的兼容性说明和发布说明。版本确认：db.version()db.serverBuildInfo()2.2 Docker 单节点docker network create mongo-netdocker run -d \  --name mongo \  --network mongo-net \  -p 27017:27017 \  -e MONGO_INITDB_ROOT_USERNAME=admin \  -e MONGO_INITDB_ROOT_PASSWORD=secret123 \  -v mongo-data:/data/db \  mongo:7.0 --bind_ip_all进入容器：docker exec -it mongo mongosh -u admin -p secret123学习环境可以固定 mongo:7.0 这类明确版本。生产环境不建议把密码直接写在命令行参数中，应使用密钥管理或容器编排 Secret。2.3 二进制安装以 Linux 为例，安装后先确认数据目录和日志目录：/var/lib/mongodb/var/log/mongodb配置文件通常为 /etc/mongod.conf：storage:  dbPath: /var/lib/mongodb  journal:    enabled: truesystemLog:  destination: file  logAppend: true  path: /var/log/mongodb/mongod.lognet:  port: 27017  bindIp: 127.0.0.1security:  authorization: enabled启动与状态：systemctl start mongodsystemctl status mongod2.4 mongosh 基础连接：mongosh "mongodb://admin:secret123@localhost:27017/admin"常用命令：show dbsuse shopshow collectionsdb.products.findOne()db.stats()创建业务数据库和集合：use shopdb.createCollection("products", {  validator: {    $jsonSchema: {      required: ["sku_id", "title", "price"],      properties: {        sku_id: { bsonType: "string" },        title: { bsonType: "string" },        price: { bsonType: "decimal" }      }    }  }})2.5 单节点副本集很多能力依赖副本集，例如事务、Change Stream 和多数写确认。开发环境可以启动单节点副本集：docker run -d \  --name mongo-rs \  -p 27017:27017 \  -v mongo-rs-data:/data/db \  mongo:7.0 \  --replSet rs0 \  --bind_ip_all \  --keyFile /etc/mongo-keyfile初始化：rs.initiate({  _id: "rs0",  members: [{ _id: 0, host: "localhost:27017" }]})查看状态：rs.status()db.hello()2.6 生产部署清单            项目      建议                  拓扑      至少三数据节点副本集，跨故障域部署              认证      启用访问控制和角色最小化              网络      只监听内网，不暴露公网              存储      SSD，数据与日志单独监控              内存      工作集尽量放得下              备份      定期快照或 dump，并演练恢复              监控      延迟、复制延迟、连接、慢查询              副本      监控 secondary lag              Schema      应用层契约 + validator              变更      低峰执行索引和升级      2.7 用户与角色创建管理员：use admindb.createUser({  user: "admin",  pwd: "secret123",  roles: [{ role: "root", db: "admin" }]})创建业务用户：use shopdb.createUser({  user: "shop_app",  pwd: "app-secret",  roles: [    { role: "readWrite", db: "shop" }  ]})生产环境应按服务拆分账号，只授予必要数据库权限。2.8 连接字符串副本集连接：mongodb://user:password@mongo1:27017,mongo2:27017,mongo3:27017/shop?replicaSet=rs0&amp;authSource=admin常用参数：            参数      说明                  replicaSet      副本集名称              authSource      认证数据库              readPreference      读路由策略              w      写关注              readConcern      读关注              connectTimeoutMS      连接超时              serverSelectionTimeoutMS      选点超时      2.9 常见连接问题            问题      排查                  Connection refused      进程未启动、端口或 bindIp 错误              Authentication failed      密码、用户库、账号错误              Not primary      连接 secondary 执行写              Node is not in primary      副本集状态异常              Timeout      网络、防火墙、DNS、选点              ReplicaSetNoPrimary      副本集初始化或选举异常      本章小结学习环境可以用 Docker 或单机 mongod 快速启动；事务、Change Stream 和高可用能力应放在副本集中验证。生产环境从第一天就要考虑认证、网络隔离、副本集拓扑、存储、监控和备份，而不是等上线后再补。思考题  为什么单机 mongod 不适合生产？  单节点副本集适合验证哪些能力？  bindIp 配置错误会带来什么问题？  业务账号为什么不应使用 root？  连接字符串中 authSource 的作用是什么？</li>
  <li>这是《MongoDB 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开，代码块保留了原书可直接运行的版本。MongoDB 是面向文档的分布式数据库。它用 BSON 文档保存数据，用集合组织文档，用丰富的索引和聚合管道提供查询分析能力，并通过副本集和分片提供高可用与横向扩展。MongoDB 不是“比 MySQL 更随意的数据库”。好的 MongoDB 设计仍然需要明确实体边界、访问模式、一致性要求、索引策略和分片键。1.1 为什么需要文档数据库一个商品有多组属性：{  "sku_id": "sku_10001",  "title": "轻薄笔记本",  "price": 5999,  "attrs": [    { "name": "CPU", "value": "16核" },    { "name": "内存", "value": "32GB" }  ],  "images": ["a.jpg", "b.jpg"],  "tags": ["轻薄", "高续航"]}如果用关系表建模，会拆成商品表、属性表、图片表和标签表。对于强事务、强约束的数据，这很合理；但对于读多写少、结构变化快、整体读取的文档，拆表会增加复杂度。MongoDB 的优势：  文档结构贴近业务对象；  数组和嵌套对象可自然表达；  Schema 更灵活，支持字段演进；  单文档 ACID 事务；  聚合管道表达能力强；  原生支持水平分片；  JSON 开发体验友好。1.2 MongoDB 的核心概念            关系型术语      MongoDB 术语      说明                  Database      Database      数据库              Table      Collection      集合              Row      Document      文档              Column      Field      字段              Primary Key      _id      文档唯一标识              Join      $lookup / Embedded      关联或内嵌              Index      Index      索引      一个文档示例：db.products.insertOne({  sku_id: "sku_10001",  title: "轻薄笔记本",  price: NumberDecimal("5999.00"),  attrs: [    { name: "CPU", value: "16核" },    { name: "内存", value: "32GB" }  ],  status: "ON_SALE",  created_at: new Date()});1.3 BSON 与 JSONMongoDB 在网络上常用 JSON，在存储层使用 BSON。BSON 是二进制 JSON，支持：  整数、浮点数、Decimal128；  Date；  ObjectId；  Binary；  RegExp；-嵌套对象和数组；  内嵌文档。{  _id: ObjectId("66cb0c7f9e2a1f2b3c4d5e6f"),  price: NumberDecimal("19.90"),  created_at: ISODate("2026-08-25T10:00:00Z"),  binary_field: BinData(0, "aGVsbG8=")}金额不要直接用 double，应使用 NumberDecimal。1.4 嵌套与引用文档建模的核心选择：            方式      特点      适合                  内嵌 Embedded      一次读取完整对象      一对少、整体读取              引用 Reference      分开存储再关联      一对多、多对多、大对象              混合      常用部分内嵌，历史部分引用      复杂业务      内嵌示例{  user_id: "u_1001",  name: "Alice",  addresses: [    { type: "home", city: "Shanghai" },    { type: "work", city: "Beijing" }  ]}适合：地址数量有限，查询用户时通常一起返回。引用示例// users{ _id: "u_1001", name: "Alice" }// orders{  order_id: "o_10001",  user_id: "u_1001",  amount: NumberDecimal("199.00")}适合：订单数量无限增长，不能全部内嵌到用户文档。建模原则：  按查询建模，而不是按实体关系机械翻译；  避免无界数组；  大字段和低频字段考虑拆分；  高频更新的计数不要放在大文档里；  单文档大小上限通常按 16MB 设计，但生产应远小于该值。1.5 MongoDB 架构概览单机 MongoDB 由 mongod 进程提供服务，生产环境通常使用副本集。Replica Set  |-- Primary       接收写入  |-- Secondary 1   同步数据，可提供读  |-- Secondary 2   同步数据，提高可用性  +-- Arbiter       可选，只参与投票不存数据当 Primary 不可用时，具备投票权的节点会选出新 Primary。分片集群Client  -&gt; mongos Router     -&gt; Shard 1 Replica Set     -&gt; Shard 2 Replica Set     -&gt; Shard 3 Replica SetConfig Server            组件      职责                  mongos      路由请求，不存业务数据              Shard      存储数据分片              Config Server      保存集群元数据和 chunk 信息              Shard Key      决定数据分布      1.6 读写关注MongoDB 提供可调的一致性语义。Write Concerndb.orders.insertOne(  { order_id: "o_10001", amount: 100 },  { writeConcern: { w: "majority", wtimeout: 3000 } });            级别      含义                  w: 1      Primary 确认              w: majority      多数节点确认              j: true      落 journal      关键交易数据建议使用 majority 写关注。Read Concern控制读取的数据一致性级别，常见包括 local、majority、linearizable。Read Preference控制读请求路由：            模式      说明                  primary      只读 Primary              primaryPreferred      优先 Primary              secondary      只读 Secondary              secondaryPreferred      优先 Secondary              nearest      最低延迟节点      Secondary 读可能带来复制延迟，需要结合业务容忍度。1.7 MongoDB 与 MySQL 的边界            维度      MongoDB      MySQL                  数据模型      文档      关系表              Schema      灵活      强约束              事务      多文档事务可用但成本高      强项              关联      内嵌或 $lookup      JOIN              扩展      原生分片成熟      常依赖中间件或架构拆分              金额与强一致      可用但需谨慎      更常用              迭代速度      字段演进快      DDL 成本更高      适合 MongoDB：  用户画像；  商品详情；  内容管理；  行为事件；  IoT 数据；  移动端离线同步；  多变的业务配置和元数据。更适合 MySQL：  订单交易；  账户余额；  支付流水；  强约束主数据；  复杂财务对账。1.8 生产使用红线  生产必须副本集部署，不能单点；  分片键必须提前设计，事后改造成本高；  每个查询都要有索引支撑；  避免无界数组无限增长；  金额使用 Decimal128；  多文档事务要短小；  Secondary 读必须监控复制延迟；  必须启用认证、网络隔离和 TLS；  备份必须可恢复并定期演练；  慢查询和锁等待要有监控。本章小结MongoDB 以文档模型、灵活 Schema、聚合管道、副本集和分片见长。它的设计重点是“按访问模式建模”：确定读取形态，再决定内嵌或引用；确定一致性要求，再选择读写关注；确定规模和热点，再设计分片键。它不是关系型数据库的替代品，而是业务模型适配的另一种选择。思考题  什么数据适合内嵌，什么数据适合引用？  MongoDB 的副本集和分片分别解决什么问题？  为什么无界数组是危险设计？  Secondary 读会带来什么一致性风险？  如果让你把商品详情从 MySQL 迁到 MongoDB，如何设计文档结构？</li>
</ul>
