MongoDB(三):副本集、一致性读写与事务
MongoDB(三):副本集、一致性读写与事务
导语:副本集是 MongoDB 高可用的基石,也是面试分水岭——初级只会说「一主多从」,高级能讲清「选举怎么触发、oplog 为什么是幂等的、Rollback 什么时候发生、readConcern 与 readPreference 有什么区别、事务为什么必须配 majority」。本篇覆盖副本集架构、选举与故障转移、oplog 同步、四种一致性开关、多文档事务与 Change Streams,共 22 题。
一、副本集架构
1. 什么是副本集?它解决了什么问题?(必问)
答: 副本集(Replica Set)是一组保存相同数据的 mongod 进程,其中一个是 Primary,其余是 Secondary,通过自动选举与异步复制(oplog)实现:
- 高可用(自动故障转移):Primary 掉线后,多数派投票在秒级内选出新的 Primary,应用几乎无感;
- 数据冗余:多份副本,防范磁盘/机器故障;
- 读写分离:Secondary 可承担读流量与备份、报表等任务;
- 运维便利:可在不停机的情况下升级、重建索引(滚动升级)。
与 MySQL 主从的关键差异:MongoDB 的故障转移是自动的,MySQL 传统主从需要 MHA/Orchestrator 之类的工具;MongoDB 的选举是内置能力。
2. 副本集有哪些成员类型?数量上有什么限制?
答:
| 成员类型 | 说明 |
|---|---|
| Primary | 唯一接受写入的节点,记录 oplog 供从节点拉取 |
| Secondary | 复制 Primary 的 oplog 并应用,可选承担读流量 |
| Arbiter(仲裁者) | 不存数据、不参与数据同步,只参与投票,用于凑够奇数票(省钱,但降低容错能力) |
隐藏节点(hidden: true) | 优先级为 0、对客户端不可见,用于报表、备份 |
延迟节点(secondaryDelaySecs) | 故意落后主节点一段时间,用于防范误删/误操作(「后悔药」) |
限制与建议:
- 副本集最多 50 个成员,其中最多 7 个投票成员(
votes); - 投票成员数建议为奇数(3、5),避免出现「平票无法选出主」;
- 不建议用 Arbiter:它无法参与数据同步,一旦 Primary 挂掉且只剩一个 Secondary + Arbiter,数据冗余度实际只剩 1 份;生产更推荐 3 个数据节点(PSA 架构改为 PSS);
- 跨机房部署:主节点与至少一个投票成员放同机房,避免网络分区时「少数派无法选出主」,可配合
priority控制主节点位置。
// 查看副本集配置与状态
rs.conf() // 配置(成员、优先级、隐藏、延迟)
rs.status() // 运行状态(含各成员 stateStr、optime、lag)
// 修改优先级:让同机房的节点更容易成为 Primary
cfg = rs.conf()
cfg.members[0].priority = 10
rs.reconfig(cfg)
// 添加一个延迟节点(落后 1 小时,不会被选举为主)
rs.add({ host: "mongo-backup:27017", priority: 0, hidden: true, secondaryDelaySecs: 3600 })3. 副本集成员有哪些状态?
答: 常见 stateStr:
| 状态 | 含义 |
|---|---|
STARTUP / STARTUP2 | 启动中 / 正在执行初始同步 |
RECOVERING | 恢复中(同步数据、重建索引),不对外提供读 |
PRIMARY | 主节点 |
SECONDARY | 从节点,正常复制 |
ARBITER | 仲裁者(只投票) |
DOWN | 心跳失联(从其它节点视角看到) |
ROLLBACK | 正在回滚(发生了数据分叉) |
REMOVED | 已被移出副本集 |
二、选举与故障转移
4. 副本集是怎么选举出 Primary 的?(高频)
答: MongoDB 使用自研的 PV1(protocol version 1)选举协议,官方称其为 raft-like——借鉴了 Raft 的「任期(term)+ 多数派投票 + 日志新旧比较」,并做了扩展(支持 priority、Arbiter、w:majority 提交点等)。
触发选举的时机:
- Primary 与多数成员失联(心跳超时);
- Primary 主动退位(
rs.stepDown()、维护操作); - 新节点加入或
rs.reconfig(); - 没有可用的 Primary(如首次初始化)。
选举过程:
- 心跳(heartbeat):成员间每 2 秒(
heartbeatIntervalMillis)互发心跳,连续失败超过 10 秒(settings.electionTimeoutMillis)认为对方不可用; - 发起选举:Secondary 判断自己可能适合当主(数据最新、priority > 0)后,term +1,向所有投票成员拉票;
- 投票判定:投票成员只会给「自己的 oplog 不比对方旧」且「本轮 term 未投过票」的候选者投票;
- 当选条件:获得多数派(N/2 + 1)投票成员的支持;
- 优先级影响:
priority高的节点更容易当选;已有 Primary 时,若另一个节点 priority 更高且数据不落后,会触发「尽力而为」的重新选举(让高优先级节点上位)。
// 手动让 Primary 退位(选举新主),常用于维护
rs.stepDown(60) // 60 秒内不参与选举
rs.freeze(120) // 当前节点 120 秒内不参加选举
// 调整选举超时(默认 10 秒,网络抖动频繁时可放宽,但故障转移会变慢)
cfg = rs.conf()
cfg.settings.electionTimeoutMillis = 15000
rs.reconfig(cfg)选举失败常见原因(生产排查点):
- 投票成员数量不足(多数派不可达)→ 集群只读,无法写入;
- 候选者 oplog 落后太多(不满足「数据最新」);
- 只剩 1 个投票成员活着(如 PSA 架构挂了一个数据节点)→ 无法凑够多数票;
- 网络分区导致两个「少数派」都选不出主。
5. 故障转移期间应用会怎样?驱动做了什么?
答:
- 应用通过连接字符串 + 副本集名称连接时,驱动会维护拓扑信息,自动发现新主;
- 切换瞬间,正在执行的写入可能报错:
NotWritablePrimary(不再是主)、NotPrimaryNoSecondaryOk、网络错误; - 可重试写(Retryable Writes):MongoDB 3.6+ 默认开启,驱动对「可重试的写操作」自动重试一次(依赖 session),绝大多数瞬时错误对应用透明;
- 可重试读:驱动默认
retryReads=true,读操作也会自动重试; - 应用侧最佳实践:为写操作配置
w: "majority",并捕获TransientTransactionError/UnknownTransactionCommitResult做重试。
// 连接字符串:指定副本集名称,驱动才能感知拓扑
// mongodb://m1:27017,m2:27017,m3:27017/order?replicaSet=rs0&w=majority&retryWrites=true6. 什么是 Rollback(回滚)?什么时候发生?
答: 当 Primary 在未把写入复制给多数节点的情况下宕机,新 Primary 上任后,旧 Primary 重新加入时会发现自己的数据与大多数节点不一致(分叉),此时它必须丢弃(回滚)这部分「孤儿数据」,这个过程就叫 Rollback。
发生条件:写入使用了 { w: 1 }(默认)——主节点写完就返回成功,但数据还没复制出去就宕机了。
避免方式:
- 关键写入使用
{ w: "majority" }:只有多数节点确认后才返回,从机制上杜绝回滚(在多数派原则下,被多数确认的数据一定不会丢); - 读取使用
readConcern: "majority":只读「已被多数确认」的数据,避免读到可能被回滚的数据。
// 金融级写入:多数派确认 + journal 落盘
db.accounts.updateOne(
{ _id: "A-1" },
{ $inc: { balance: -100 } },
{ writeConcern: { w: "majority", j: true } }
)
// 对应的一致性读
db.accounts.find({ _id: "A-1" }).readConcern("majority")回滚后的数据会被写入 rollback 目录下的 BSON 文件(可用 mongorestore 抢救),可通过 rs.status() 与日志确认是否发生过。
三、数据同步与 oplog
7. oplog 是什么?为什么它必须是幂等的?(高频)
答: oplog(operations log)是副本集的复制日志,位于 local.oplog.rs 集合,是一个固定大小(capped)的环形集合:写入满了就覆盖最旧的记录。所有写入先写主节点的 oplog,从节点持续拉取并重放,从而实现复制。
关键参数:
| 项目 | 默认值 | 说明 |
|---|---|---|
| 大小 | 空闲磁盘的 5%,最小 990MB,最大 50GB | 可 replSetResizeOplog 在线调整 |
| 单条记录上限 | 16MB | 与文档大小一致,这是「事务不超过 16MB oplog」限制的由来 |
| oplog window | 不固定 | 指 oplog 覆盖的时间跨度,需 > 最长故障恢复时间 |
rs.printReplicationInfo() // 主节点:oplog 大小与时间窗口
rs.printSecondaryReplicationInfo() // 从节点:各成员延迟
// 在线调整 oplog 大小到 20GB
db.adminCommand({ replSetResizeOplog: 1, size: 20480 })为什么幂等是关键? 从节点重放 oplog 时可能重复应用(重试、断点续传),如果 oplog 里存的是 $inc: {count: 1} 这种非幂等操作,重放两次就多加了 1。因此 MongoDB 在写入 oplog 前会把操作转换成幂等形式:
$inc: { count: 1 }→ 转换为$set: { count: 42 }(记录结果值);$push→ 转换为带索引定位的$set(记录数组的具体位置与值);- 这样无论重放多少次,结果都一致。
oplog 的其它考点:
- oplog 记录的是数据的最终变更,不是 SQL/命令本身;
local数据库不参与复制(oplog 与local.开头的集合不入 oplog);- 从节点默认不写 oplog 到自己的 oplog(只记录自己产生的变更);
- oplog 太小是生产事故:主节点故障时间超过 oplog window 后,从节点无法增量追上,只能重新做初始同步。
8. 初始同步(Initial Sync)是怎么做的?
答:
- 克隆(Clone):从源节点复制全量数据(所有集合、索引)到新节点;
- 应用增量:在克隆期间产生的新变更被缓存,克隆完成后重放 oplog 追平;
- 追平后进入正常的
SECONDARY状态,持续拉取 oplog。
优化与替代方案(生产实用):
- 种子节点 / 文件系统快照:先把数据文件 rsync 或从 LVM/EBS 快照复制到新节点,再启动并让它只追增量,大幅缩短同步时间;
rs.syncFrom()可指定从哪个节点同步,避免抢占主节点资源;- 初始同步期间节点处于
STARTUP2/RECOVERING,不提供读。
// 查看各个从节点的同步进度与延迟
rs.status().members.map(m => ({
name: m.name, state: m.stateStr,
lagSec: (m.optimeDate && rs.status().date) ? Math.round((rs.status().date - m.optimeDate) / 1000) : 0
}))9. 从节点同步延迟(replication lag)怎么排查?
答: 常见原因与对策:
| 原因 | 表现 | 对策 |
|---|---|---|
| 从节点硬件/磁盘更差 | 持续 lag | 升级硬件、使用相同配置 |
| 写入量超过从节点重放能力 | oplog 写入速率高 | 扩容分片、优化写模式(ordered:false) |
| 从节点在做大查询/备份 | 周期性 lag | 备份/报表放在隐藏节点 |
| 网络带宽不足 | 拉取 oplog 慢 | 同机房部署、增加带宽 |
| 从节点频繁重建索引/初始同步 | RECOVERING | 避开高峰、用快照加速 |
rs.printSecondaryReplicationInfo() // 直观看到每个从节点的延迟秒数
db.serverStatus().metrics.repl.buffer // 8.0 起为双缓冲,可观察 oplog 缓冲情况四、读写关注与一致性(最容易混淆的一组概念)
10. readPreference 有哪几种?它解决什么问题?
答: readPreference 决定读请求发给谁(路由问题),与数据一致性无关:
| 模式 | 行为 |
|---|---|
primary(默认) | 只读主节点,强一致(能看到最新写入) |
primaryPreferred | 优先主节点,主不可用时读从 |
secondary | 只读从节点,可能读到旧数据 |
secondaryPreferred | 优先从节点,从不可用时读主 |
nearest | 读网络延迟最低的节点(可能是主也可能是从) |
// 连接级设置
// mongodb://...?readPreference=secondaryPreferred&maxStalenessSeconds=90
// 查询级设置
db.orders.find({ userId: "U-1" }).readPref("secondaryPreferred")
// 聚合中也可以指定(如报表查询走从节点)
db.orders.aggregate([...], { readPreference: "secondary" })11. readConcern 有哪几种?和 readPreference 有什么区别?(必问)
答: readConcern 决定「读什么数据」——读到的数据是否已被多数节点确认、是否参与回滚(隔离/一致性级别)。
| 级别 | 含义 | 适用 |
|---|---|---|
local(默认) | 读本节点当前拥有的数据,可能被回滚 | 一般查询 |
available | 读本节点数据,不保证与分片一致(可能读到孤儿文档) | 分片集群中追求低延迟 |
majority | 只读已被多数节点确认的数据,绝不会被回滚 | 金融、账务、关键的「写后读」 |
linearizable | 线性一致读,保证读到「所有已完成写入」的最新值 | 单文档强一致读,只支持主节点,开销大 |
snapshot | 读事务开始时的一致性快照 | 多文档事务专用 |
一句话区分:
readPreference决定「去哪读」,readConcern决定「读多新的数据」。 两者组合使用:readPreference=secondary+readConcern=majority表示「从从节点读,但只读已被多数确认、不会被回滚的数据」。
// 组合使用:从从节点读,但保证读到的数据不会被回滚
db.orders.find({ orderNo: "1001" }).readPref("secondary").readConcern("majority")补充考点:
local+secondary:可能读到回滚前的脏数据(虽然概率低,但机制上允许);linearizable只对单文档读有效,且必须readPreference=primary,实现上要比对多数节点,代价高,不要滥用;snapshot用于事务(startTransaction时自动使用)。
12. writeConcern 有哪几个参数?(必问)
答:
| 参数 | 含义 |
|---|---|
w | 需要多少个节点确认。0=不确认;1=主节点确认(默认);"majority"=多数投票成员确认;也可写具体数字 n |
j | 是否等待写入 journal 落盘后才确认。j:true 才具备真正的持久化保证 |
wtimeout | 等待确认的超时(毫秒),超时只是返回错误,写入本身不会回滚 |
// 四种典型组合与语义
db.coll.insertOne({ a: 1 }) // w:1(默认):快,但可能丢
db.coll.insertOne({ a: 1 }, { writeConcern: { w: "majority" } }) // 多数派:不会被回滚
db.coll.insertOne({ a: 1 }, { writeConcern: { w: "majority", j: true } }) // 多数派 + 落盘(最强)
db.coll.insertOne({ a: 1 }, { writeConcern: { w: 0 } }) // 不确认(fire-and-forget):最快,可能丢且无错误反馈要点:
w: "majority"是「不丢数据」的关键:它保证写入被多数节点记录,从而不会发生 Rollback;j: true与w是独立维度:{ w: 1, j: true }只是「主节点落盘」,主节点宕机后仍可能因为数据没复制而丢失;- MongoDB 5.0 起:
w: "majority"隐含j: true(更强的默认保证); - MongoDB 8.0 起:
w: "majority"在多数成员写入 oplog 后即确认(此前需等多数成员「应用」变更),写入更快,但「写完立刻从从节点做非因果一致读」可能读不到——需用因果一致会话或readConcern: majority兜底; wtimeout不取消写入:它只让客户端提前收到「未在时限内确认」的错误,写入可能仍在后台成功,因此重试前务必考虑幂等性。
13. 如何保证「写后立刻能读到」?(场景题,高频)
答: 这是分布式数据库的经典问题,MongoDB 有四种解法,按成本从低到高:
方案 1:写读都走主节点(最简单)
// 默认行为即是如此:readPreference=primary方案 2:写用 majority,读用 majority + 从节点
// 写入
db.orders.insertOne(doc, { writeConcern: { w: "majority" } })
// 读取(可以走从节点,但只读多数确认的数据)
db.orders.find(filter).readPref("primaryPreferred").readConcern("majority")方案 3:因果一致性会话(causal consistency,官方推荐)
同一个 session 内保证「读能观察到之前的写」,跨节点也成立:
const session = db.getMongo().startSession({ causalConsistency: true })
const coll = session.getDatabase("order").orders
coll.insertOne({ orderNo: "1001" }) // 写
coll.findOne({ orderNo: "1001" }) // 同一 session 内必能读到
session.endSession()Java 驱动中:
MongoClientOptions默认开启因果一致(causalConsistency默认 true 的会话),且writeConcern=majority+readConcern=majority时效果最好。
方案 4:不用从节点读
对一致性极其敏感的业务(余额、库存),直接读主节点,把一读多的场景(列表、报表)留给从节点。
面试回答模板:「先明确一致性要求——如果必须强一致,读主;如果需要读写分离又要求不读到回滚数据,就用
w:majority+readConcern:majority;如果要求『本会话内写后可读』,用因果一致性 session。」
五、多文档事务
14. MongoDB 事务怎么用?(必问)
答: MongoDB 4.0 支持副本集多文档事务,4.2 扩展到分片集群,提供 ACID 保证(隔离级别为快照隔离)。
// 方式一:显式开启/提交(灵活处理错误)
const session = db.getMongo().startSession()
session.startTransaction({
readConcern: { level: "snapshot" }, // 事务必须用 snapshot
writeConcern: { w: "majority" } // 事务必须用 majority
})
try {
const accounts = session.getDatabase("bank").accounts
accounts.updateOne({ _id: "A" }, { $inc: { balance: -100 } })
accounts.updateOne({ _id: "B" }, { $inc: { balance: 100 } })
session.commitTransaction()
} catch (e) {
session.abortTransaction()
throw e
} finally {
session.endSession()
}
// 方式二:withTransaction —— 自动处理「可重试错误」与提交重试(推荐)
db.getMongo().startSession().withTransaction(function (session) {
const accounts = session.getDatabase("bank").accounts
accounts.updateOne({ _id: "A" }, { $inc: { balance: -100 } })
accounts.updateOne({ _id: "B" }, { $inc: { balance: 100 } })
})Java 驱动写法(面试常被要求手写):
try (ClientSession session = client.startSession()) {
session.withTransaction(() -> {
accounts.updateOne(session, Filters.eq("_id", "A"), Updates.inc("balance", -100));
accounts.updateOne(session, Filters.eq("_id", "B"), Updates.inc("balance", 100));
return null;
}, TransactionOptions.builder()
.readConcern(ReadConcern.SNAPSHOT)
.writeConcern(WriteConcern.MAJORITY)
.build());
}15. MongoDB 事务的实现原理是什么?
答: 三个关键词:
- 快照隔离(Snapshot Isolation):事务开始时获得一个一致性快照(基于 WiredTiger 的 MVCC),事务内的所有读都看到同一版本的数据,不会读到其他事务未提交的修改;
- WriteConflict 与自动重试:因为 WiredTiger 是乐观并发控制,两个事务修改同一文档时,后提交者会收到
WriteConflict错误(被归类为TransientTransactionError),应用/withTransaction应重试整个事务; - 副本集多数派提交:事务的提交最终以 oplog 的形式复制到从节点,因此必须使用
writeConcern: majority—— 事务的原子性通过「所有变更作为一组写入 oplog」来表达。
一致性代价:
- 事务跨多个文档/集合/分片时,需要锁与协调开销,比单文档更新慢一个量级;
- 分片集群的跨分片事务需要在 mongos 与各分片间做协调,尽量避免让事务跨分片(让相关文档落在同一个分片上)。
16. 事务有哪些限制和最佳实践?(高频)
答:
限制
| 项目 | 限制 |
|---|---|
| 生命周期 | 默认 60 秒(transactionLifetimeLimitSeconds),超时自动中止 |
| 单条 oplog 条目 | 16MB,所以单个事务内的写入总量不能超过 16MB |
| 隔离级别 | 固定为快照隔离,不能选读未提交/读已提交 |
| 读关注 | 事务内必须 snapshot;写关注必须 majority |
| 冲突 | 并发修改同一文档会 WriteConflict,必须做好重试 |
| 不支持的操作 | 不能在事务中执行 createUser、dropDatabase、listCollections 等管理操作;system.* 集合不可操作 |
| 创建集合/索引 | 4.0/4.2 不允许;4.4 起支持(隐式创建集合与索引) |
$lookup | 分片集合之间的事务中使用 $lookup 需要 8.0+ |
最佳实践
- 能不事务就不事务:优先通过内嵌文档让一组数据变成单文档操作(单文档天然原子);
- 事务要短小:不要在里面做 HTTP 调用、外部 RPC、大批量写入;
- 必须做重试:捕获
TransientTransactionError(重试整个事务)、UnknownTransactionCommitResult(重试提交); - 读写都要有索引:事务内的查询若走 COLLSCAN,会长时间持有快照、加剧冲突;
- 控制并发:热点文档的事务并发要限制,否则 WriteConflict 会让重试风暴出现;
- 分片场景尽量让事务落在单个分片:把关联数据用同一分片键分布;
- 监控:
db.serverStatus().transactions观察 started/committed/aborted 数量,aborted 比例高说明冲突严重。
// 观察事务指标
db.serverStatus().transactions
// { currentActive: 0, currentInactive: 0, currentOpen: 0,
// totalStarted: 1520, totalCommitted: 1480, totalAborted: 40, ... }面试对比题:「MongoDB 事务和 MySQL 事务有什么区别?」——回答要点:MongoDB 事务是后期补足能力(4.0 才支持多文档),隔离级别固定为快照隔离,默认 60 秒超时、受 16MB oplog 限制,适用场景是「偶发的一致性需求」;MySQL 事务成熟、隔离级别可选、更适合长事务与复杂约束。能用数据建模避免事务的,就不要用事务。
六、Change Streams 与备份
17. Change Streams 是什么?原理与应用场景?
答: Change Streams(变更流,3.6+)让应用可以实时订阅集合/库/整个集群的数据变更,基于 oplog 实现(因此必须部署为副本集或分片集群)。
// 订阅某集合的变更(只关心订单表的插入与更新)
const pipeline = [
{ $match: { "operationType": { $in: ["insert", "update"] },
"ns.coll": "orders" } }
]
const changeStream = db.orders.watch(pipeline, { fullDocument: "updateLookup" })
while (changeStream.hasNext()) {
const change = changeStream.next()
print(`${change.operationType} @ ${change.documentKey._id} -> ${JSON.stringify(change.fullDocument)}`)
// resumeToken 用于断点续传(故障后从上次位置继续)
print("resumeToken: " + JSON.stringify(change._id))
}要点:
| 特性 | 说明 |
|---|---|
| 事件类型 | insert / update / replace / delete / drop / rename / invalidate |
fullDocument | 默认只给出 documentKey;updateLookup 会回查完整文档 |
| resumeToken | 每个事件带唯一标记,用 resumeAfter / startAtOperationTime 实现断点续传 |
| 一致性 | 需要 readConcern: majority,保证只看到已提交且不会回滚的变更 |
| 典型场景 | 缓存失效、同步到 ES/数仓、实时通知、审计、触发器(Atlas Triggers) |
与 oplog 直接读取的区别:Change Streams 是受支持的 API(有过滤、有 resume、跨版本稳定、分片集群下自动合并各分片事件),而直接读 local.oplog.rs 是内部实现,不保证兼容。
18. 生产环境怎么做备份与恢复?
答:
| 方式 | 说明 | 适用 |
|---|---|---|
mongodump / mongorestore | 逻辑备份,逐文档导出 BSON;对生产有资源压力,恢复慢 | 小数据量、单集合恢复、跨版本迁移 |
| 文件系统快照 | LVM/EBS/云盘快照,秒级完成,对生产几乎无影响 | 大数据量的主力手段(结合 journal 保证一致性) |
| Oplog 增量恢复 | 快照 + 之后的 oplog,可恢复到任意时间点(PITR) | 需要「误删前一刻」的精确恢复 |
| 云备份 / Atlas | 自动化、有 PITR、按策略保留 | 云环境推荐 |
# 逻辑备份与恢复(指定库和集合,减少影响)
mongodump --uri="mongodb://user:pwd@m1:27017/?replicaSet=rs0" -d order -c orders -o /backup/20260925
mongorestore --uri="mongodb://..." -d order /backup/20260925/order
# 恢复时不重建索引以加速(之后再单独建索引)
mongorestore --noIndexRestore --uri="mongodb://..." /backup/20260925关键经验:
- 备份务必指定从节点/隐藏节点(
--readPreference=secondary),避免影响主节点; - 备份文件一定要验证可恢复(定期演练);
- 单集合恢复可用
mongorestore --nsInclude; - 有
majority写关注时,快照备份的数据不会包含将被回滚的数据。
19. 副本集生产运维要看哪些指标?
答:
| 类别 | 指标 | 关注点 |
|---|---|---|
| 复制 | replication lag、oplog window | lag 持续增长、window 小于计划内最长故障恢复时间 → 危险 |
| 成员状态 | rs.status() 中是否有非 PRIMARY/SECONDARY 状态 | ROLLBACK、RECOVERING 异常增多 |
| 连接 | serverStatus().connections | current 接近 available 说明连接池耗尽 |
| 内存/缓存 | wiredTiger.cache 的 bytes currently in the cache、tracked dirty bytes | 脏页长期偏高说明刷盘跟不上 |
| IO | extra_info.page_faults、磁盘 util | 持续 page fault 说明内存不足 |
| 查询 | 慢查询数量、planSummary: COLLSCAN 占比 | 全表扫描是最大杀手 |
| 锁/事务 | WriteConflict、事务 aborted 比例 | 冲突高说明热点过于集中 |
db.serverStatus().connections // current / available / totalCreated
db.serverStatus().wiredTiger.cache // 缓存与脏页
db.serverStatus().opcounters // 各类操作计数(insert/query/update/delete)
db.serverStatus().metrics.cursor // 打开游标数(暴增常意味着游标泄漏)七、小结
| 概念 | 一句话记忆 |
|---|---|
| 副本集 | 一主多从 + 自动选举,奇数投票成员,最多 7 个投票节点 |
| 选举 | PV1(raft-like),多数派投票 + 数据最新 + priority 高 |
| oplog | 固定大小环形日志,写入前转为幂等操作,太小会导致无法增量追平 |
| Rollback | 只在未用 majority 时发生,用 w:majority + readConcern:majority 根治 |
| readPreference | 决定「去哪读」:primary / secondary / nearest… |
| readConcern | 决定「读多新」:local / majority / linearizable / snapshot |
| writeConcern | w(几个节点确认)+ j(是否落盘)+ wtimeout(超时) |
| 事务 | 4.0 副本集 / 4.2 分片,快照隔离,60 秒 + 16MB 限制,能不用就不用 |
| Change Streams | 基于 oplog 的官方变更订阅,带 resumeToken 可断点续传 |
