MongoDB速记

MongoDB 核心考点速记笔记(锚定实习 RBAC 版本模型代码)

定位:写在 za-lincoln 技术栈里必被问。目标------概念题秒答,且每题都能落到"我实习怎么用的"。


一、MongoDB 是什么 & 与 MySQL/Redis 选型(必考第一问)

  • 定位: 面向文档的分布式数据库(BSON 文档 = 二进制 JSON),Schema 灵活(同集合文档结构可不同),原生支持水平扩展(分片)与副本集高可用。

  • 三选型一句话:

    • MySQL: 关系强、多表 JOIN、事务强一致(钱、账)→ 结构化核心数据。
    • MongoDB: 写多读少或结构多变、嵌套层次深、聚合统计(日志、配置版本、画像、内容)→ 免 JOIN 的对象型数据。
    • Redis: 纯缓存/高频读写,数据可丢或可重建。
  • 实习锚点: RBAC 权限版本(roles 是嵌套 Map:角色→资源→操作三层嵌套)------关系库要拆 5 张表 + 5 次 JOIN,MongoDB 一个文档一次读出,这就是选它的理由(面试原话)。


二、文档模型核心概念(对齐 MySQL 词汇表)

MySQL MongoDB
Database Database
Table Collection
Row Document(BSON)
Column Field
主键 _id(默认 ObjectId:12 字节 = 4 时间戳 + 5 机器随机 + 3 计数器)
JOIN 嵌入式文档 (或 $lookup,性能差少用)
  • 嵌套 vs 引用(经典设计题): "一起读、强从属" → 嵌入( RBAC 的 roles);"独立生命周期/多对多" → 引用(分集合 + 应用层组装)。
  • 反范式陷阱: 嵌入文档无限膨胀(16MB 上限)→ 大数组要拆出集合。

三、必备查询与更新语法(写过的才敢说熟)

  • 查询: db.col.find({age: {$gt: 18}}, {name: 1})$in/$ne/$exists数组匹配是"包含"语义{tags: "a"} 匹配数组含 a 的文档)。

  • 投影: 第二个参数选字段(_id 默认带,_id: 0 关掉)。

  • 更新三兄弟(辨析必考):

    • updateOne/Many + $set (只改指定字段;不写 $set整文档替换------新手大坑)。
    • $setOnInsert 只在 upsert 插入新文档时生效,已有文档不动------RBAC 草稿创建用它保护已存在草稿的字段不被覆盖。
    • $inc 原子自增(计数器场景)。
  • upsert: 查到则更新、没查到则插入(upsert: true)------必须配唯一索引 防并发双插(与 MySQL ON DUPLICATE KEY UPDATE 同思想,见项目篇-1)。

  • findAndModify(实习核心武器): "查找+修改+返回修改后文档"一条原子命令 。RBAC 的 getOrCreateDraftRolefindAndModify + upsert + returnNew 实现"存在草稿就返回、没有就创建"------不加锁天然幂等 ,等价于"带返回值的原子 upsert"(比 update + 再查一次 的两步竞态安全)。


四、索引与执行计划

  • 索引类型: 单字段、复合索引 (同样遵循最左前缀)、唯一索引 (防重)、TTL 索引(到期自动删文档,日志/会话场景)、文本/Atlas 全文索引(一般交给 ES,别硬扛)。
  • 索引底层: B+ 树(和 MySQL 一样,3.0+ WiredTiger 引擎;早期 MMAPv1 是 B 树------说"Mongodb 是 B 树"是老黄历)。
  • explain: db.col.find(...).explain("executionStats"),看 totalDocsExamined vs nReturned(扫描/返回比 = 索引效率)。
  • 慢查询三板斧: profiler(db.setProfilingLevel(1, {slowms: 100}))→ explain 看是否全扫 → 补索引/改查询。

五、事务与一致性(高频追问)

  • 单文档原子性: 单文档的更新(含嵌套数组操作、findAndModify)天然原子------多数场景根本不需要多文档事务(这是 MongoDB 的设计哲学,先说这句加分)。
  • 多文档事务: 4.0 起副本集支持、4.2 起分片支持,用法 session.startTransaction()代价: 性能差(跨节点协调)、默认 60s 超时------能用单文档原子 + 应用层补偿就不用多文档事务
  • 实习锚点: RBAC 版本激活"先置新 VALID 再废旧 VALID"两步用 BulkWrite 一次提交 ,且顺序故意反着来 :先废后立会出现"无 VALID 版本"窗口(查询报错),先立后废只是"暂时双 VALID"(查询仍能返回)------用顺序设计规避分布式事务,面试讲出来就是设计功底。
  • 写关注(Write Concern): w:1(主节点确认,默认)/ w:majority(过半确认,防主从切换丢写)/ w:0(不等确认,最快)。要求不丢写 → majority + journal。

六、副本集与分片(架构层必考)

1. 副本集(Replica Set)------ 高可用

  • 角色: 1 Primary(唯一写入口)+ N Secondary(同步复制 oplog)。
  • 选举: Sentinel 式?不------基于 Raft 思想 的自身协议,多数派投票选出新主(默认约 10s 心跳超时,任期 term 防脑裂)。类比记忆:MySQL 主从 + Raft 选举的合体
  • Secondary 三种形态: 普通备份 / Priority 0 (永不参选)/ Hidden(对客户端不可见,专给报表)。
  • 读写分离: readPreference=secondaryPreferred 读从库------接受最终一致,写永远走主。
  • oplog: 主库的操作日志(幂等可重放的幂等性 op),从库拉取回放------类比 MySQL binlog(但 oplog 是幂等格式,重放安全)。

2. 分片(Sharding)------ 水平扩展

  • 三大组件: mongos(路由,无状态)+ Config Server(元数据,副本集)+ Shard(数据片,各自是副本集)。
  • 分片键(Shard Key): 决定文档落哪个片。必须建在分片键上的查询才只路由到单片(否则广播全片)。
  • 两类策略: 范围分片 (range,易理解但易热点------单调递增键全打到最后一片)vs 哈希分片(hash,均匀但牺牲范围查询)。默认哈希更常用。
  • vs 手动分库分表(MySQL): Mongo 分片是数据库原生自动的(应用无感知、自动均衡 rebalance),MySQL 要中间件/应用层搞------这就是"结构多变 + 需要水平扩展"场景选 Mongo 的第二个理由。
  • jumbo chunk / 热点: 单个分片键值过大无法分裂 → 选高基数(取值多)的分片键。

七、存储引擎与内存(WiredTiger)

  • WiredTiger(3.2+ 默认): 文档级并发控制(读写锁粒度到文档,高并发写入强)+ 快照隔离 MVCC + B+ 树索引。
  • 缓存: 默认约 (RAM - 1GB) / 2 为 WiredTiger Cache------MongoDB 吃内存是设计使然(热数据全在 cache 才快),生产独占机器、别和 MySQL 挤一台。
  • 日志: journal(WAL 预写日志,崩溃恢复,类比 InnoDB redo log)+ checkpoint。

八、Aggregation 聚合管道(替代复杂 SQL 的武器)

  • 管道思想: $match → $group → $sort → $limit 一条管道流式处理,等价于 SQL 的 WHERE/GROUP BY/ORDER BY。
  • 高频算子: $match(尽量放最前面,先过滤少过数据)、$group + $sum/$avg$project(投影)、$unwind(数组炸开成多行)、$lookup(左连接,能不用就不用)。
  • 实战锚点: RBAC 的"角色数量、权限覆盖度统计"(findAllApiResources 后算覆盖)就是聚合管道------和 SQL 对着讲("把 GROUP BY 换成 $group 而已")。

九、实习项目深挖预案(背熟这三问三答)

  1. "为什么 RBAC 用 MongoDB 不用 MySQL?"

    "三层嵌套结构(角色→资源→操作)在关系库要拆 5 张表 + 5 次 JOIN,读一个完整版本要拼装;Mongo 一个文档整存整取,且版本快照这种'写少读多、按版本整体读取'的模式和文档模型天然匹配。事务弱点的风险我们用单文档原子操作 + 顺序设计(先立后废)规避了。"

  2. "并发发布两个草稿怎么办?"

    "Redis 分布式锁 + 锁内双重校验 + Mongo 版本号自增;草稿获取本身用 findAndModify+upsert 原子幂等,连锁都不用。"

  3. "MongoDB 事务了解吗?"

    "单文档天然原子;多文档 4.0+ 支持但我们没用------BulkWrite 一次提交两步更新 + 反序设计替代了它,性能更好。"


十、自测清单

  • 文档 vs 关系选型的三个判据?ObjectId 的组成?
  • findAndModify + upsert 为什么是原子的?$setOnInsert 干嘛的?
  • 先废后立 vs 先立后废的窗口差异?BulkWrite 是原子的吗?
  • 副本集选举基于什么思想?oplog 类比 MySQL 什么?为什么是幂等的?
  • 分片键两类策略的取舍?什么查询会广播?
  • WiredTiger 的并发控制粒度?为什么 Mongo 吃内存是正常的?
相关推荐
Gopher_HBo1 小时前
beego 配置模块(core/config)
后端
freewcx1 小时前
StringRedisTemplate
后端
小园子的小菜1 小时前
LangChain核心进阶解析:输出解析、聊天记忆与回调机制实战
后端
freewcx1 小时前
SpringDataRedis的序列化方式
后端
YHL1 小时前
🚀 NestJS 后端开发实战:从设计模式到 CRUD 全栈
后端
晚安日记wanna1 小时前
接口幂等性怎么保证
后端·面试
风中的小熊生气1 小时前
Spring Boot 面试知识(一):从启动流程到 AOP 与事务失效
spring boot·后端·面试
神秘的猪头1 小时前
Gin 项目为什么要分 Handler、Service、Repository?顺便讲透接口、依赖注入与指针
后端·go
程序员cxuan1 小时前
DeepSeek V4.1 Flash 正式发布!
人工智能·后端·程序员