04 特色及优势

维度能力
数据模型多形性(同集合不同字段)、动态性(在线改模式)、JSONSchema 治理
开发体验单存储区读写、反范式 + 无关联优化、API 自然
高可用Replica Set(2~50 节点,建议奇数)、自恢复、多中心容灾、滚动维护
横向扩展无缝扩容、应用透明、多种数据分布策略,可达 TB-PB

06 基本操作

环境与样本数据:

tar -xvf dump.tar.gz
mongorestore --uri="mongodb://root:root@10.130.0.12/?&authMechanism=SCRAM-SHA-1"

CRUD

// 插入
db.fruit.insertOne({ name: "apple" })
db.fruit.insertMany([{ name: "apple" }, { name: "pear" }, { name: "orange" }])

// 查询
db.customers.find({ username: "fmiller", name: "Elizabeth Ray" })
db.customers.find({ username: /^f/ })
db.customers.find({ $or: [{ username: /^f/ }, { name: /^E/ }] })

// 投影(projection)
db.customers.find({ username: /^f/ }, { name: 0, email: 0 })   // 排除
db.customers.find({ username: /^f/ }, { _id: 1, name: 1 })     // 仅返回

// 删除
db.customers.remove({ username: "abrown" })

// 更新
db.customers.updateOne({ username: "fmiller" }, { $set: { from: "China" } })
db.customers.updateMany({ username: "fmiller" }, { $set: { from: "China" } })

// 表 / 库管理
db.fruit.drop()
show collections
db.dropDatabase()
show dbs

SQL ↔ Mongo 操作符对照

SQLMongo
a <> 1{a: {$ne: 1}}
a > 1 / >={a: {$gt: 1}} / {$gte: 1}
a < 1 / <={a: {$lt: 1}} / {$lte: 1}
a = 1 OR b = 1{$or: [{a: 1}, {b: 1}]}
a IS NULL{a: {$exists: false}}
a IN (1, 2, 3){a: {$in: [1, 2, 3]}}
同时满足子文档条件{$elemMatch: {city: "Rome", country: "USA"}}

常用更新操作符

操作含义
$set / $unset设置 / 移除字段
$push / $pop数组追加 / 弹出
$pull / $pullAll按匹配从数组中删除
$addToSet不存在则添加,去重

08 聚合查询

Aggregation Framework:

pipeline = [stage1, stage2, ...]
db.<collection>.aggregate(pipeline, { option })

常用 stage:

Stage作用
$match过滤
$project投影
$sort排序
$group分组
$skip / $limit分页
$lookup左外连接

09 聚合查询实验

// 全集合总计
db.orders.aggregate([
    { $group: { _id: null, total: { $sum: "$total" } } }
])
// { _id: null, total: NumberDecimal("44019609") }

// 2019 Q1 已完成订单总金额(订单金额 + 运费)和订单数
db.orders.aggregate([
    {
        $match: {
            status: "completed",
            orderDate: { $gte: ISODate("2019-01-01"), $lt: ISODate("2019-04-01") }
        }
    },
    {
        $group: {
            _id: null,
            total: { $sum: "$total" },
            shippingFee: { $sum: "$shippingFee" },
            count: { $sum: 1 }
        }
    },
    {
        $project: {
            grandTotal: { $add: ["$total", "$shippingFee"] },
            count: 1,
            _id: 0
        }
    }
])
// { count: 5875, grandTotal: NumberDecimal("2636376") }

10 复制集机制及原理

角色

节点数量作用
PRIMARY1接收写入、参与选举投票
SECONDARY2+(建议奇数)复制主节点新数据、参与投票

集群上限 50 节点,有投票权的节点最多 7 个

数据复制

  • 主节点把写操作(经必要转换)写入 oplog
  • 从节点通过在主节点上打开 tailable cursor 不断拉 oplog,本地回放。

故障恢复(选举)

  • 有投票权的节点两两心跳。
  • 连续 5 次心跳未收到判定失联。
  • 主节点失联 → 触发选举;从节点失联 → 不选举。
  • 基于 RAFT 一致性算法,需 多数投票节点存活

当选条件

  • 与多数节点建立连接。
  • oplog 足够新。
  • 优先级足够高(若有配置)。

节点选配项

参数作用注意
v是否具有投票权
priority优先级(越高越优先成为 primary)priority = 0 永不成为主节点
hidden复制数据但对应用不可见可投票,但 priority 必须为 0
slaveDelay复制 N 秒前的数据(延迟节点)

注意事项

  • 硬件:节点配置必须一致(地位对等);不同节点的硬件需相互独立(避免同时宕)。
  • 软件:所有节点版本必须一致。
  • 增加节点 不会 提升写性能。

11 搭建 MongoDB 复制集

mkdir -p runtime/data_db{1,2,3} \
      && mkdir -p runtime/data_configdb{1,2,3}

hostname -f

rs.status()

12 全家桶

13 模型设计基础

三要素:Entity(实体)/ Attribute(属性)/ Relationship(关系)

三层模型映射:

层次概念对应内容
概念 CDM业务对象对象
逻辑 LDM实体 / 属性 / 关系实体、属性、关系
物理 PDM数据库结构表结构、字段列表、主外键

14 JSON 文档模型设计

无模式:物理建模过程可省。

设计原则:

  • Performance 性能
  • Ease of Development 开发易用

15 基础设计

流程:业务需求 / 逻辑模型 ──逻辑导向──→ 基础建模 → 集合 / 字段 / 基础形状 → 引用及关联 → 最终模式。

约束:

  • 单文档最大 16 MB
  • 默认 内嵌为主

16 工况细化

流程:技术需求 / 读写比例 ──技术导向──→ 工况细化 → 引用及关联。

引用模式 $lookup

db.contacts.aggregate([
  { $lookup: {
      from: "groups",
      localField: "groups_ids",
      foreignField: "groups_id",
      as: "groups"
  }}
])

何时改用引用

  • 内嵌文档太大。
  • 内嵌文档或数组元素频繁修改。
  • 内嵌数组元素持续增长且无封顶。

引用方式的限制

  • 没有主外键约束检查。
  • $lookup 只支持 left outer join
  • $lookupfrom 不能是分片表。

17 模式套用

流程:经验 / 学习 ──模式导向──→ 套用设计模式 → 优化的模型。

分桶设计(Bucket Pattern):时序数据按时间段聚合到一个文档内。

  • 大幅减少文档总数。
  • 大幅减少索引占用空间。

18 设计模式集锦

场景模式做法
字段繁多、索引爆炸(多语言多国家属性)列转行把列转成数组元素,一条索引解决所有该类查询
模式持续演进,需管理不同版本Schema Version文档加 schema_version 字段
网页点击流量等粗粒度统计近似计算if random(0,9) == 0 increment by 10
业绩 / 游戏排名等精确统计预聚合模型中加统计字段,每次写入时同步更新

19 写操作事务 writeConcern

Write Concern | mongodb

{ w: <value>, j: <boolean>, wtimeout: <number> }

w 取值:

  • 1:写入主节点即返回。
  • 数字 n:等待 n 个节点确认。
  • "majority":等待多数节点确认。
db.test.insert({ count: 2 }, { writeConcern: { w: "majority" } })
db.test.insert({ count: 2 }, { writeConcern: { w: 3 } })
db.test.insert({ count: 2 }, { writeConcern: { w: 4 } })
// [Error] 100 - Not enough data-bearing nodes

实验:延迟节点 + writeConcern 超时

// 配置延迟节点(5s 延迟、不参选)
conf = rs.conf()
conf.members[2].slaveDelay = 5
conf.members[2].priority = 0
rs.reconfig(conf)

db.test.insert({ count: 2 }, { writeConcern: { w: 3 } })
// → 等待 5s 才返回

db.test.insert({ count: 5 }, { writeConcern: { w: 3, wtimeout: 3000 } })
// → writeConcernError: waiting for replication timed out

20 读操作事务 readPreference

参考:

三种配置方式:

方式写法
连接串mongodb://.../?replicaSet=rs&readPreference=secondary
驱动 APIMongoCollection.withReadPreference(ReadPreference readPref)
Mongo Shelldb.collection.find({}).readPref("secondary")

实验:从节点写锁后读结果差异

// 主节点
db.test.insert({ count: 2 }, { writeConcern: { w: 3 } })  // 三副本确认
db.test.find({}).readPref("secondary")                    // 1 line

// 从节点 1、2
db.fsyncLock()                                             // 锁住写入

// 主节点
db.test.insert({ count: 3 })                              // 只写主
db.test.find({})                                          // 2 lines
db.test.find().readPref("secondary")                      // 1 line ← 从未同步

// 从节点 1、2
db.fsyncUnlock()
// 主节点
db.test.find().readPref("secondary")                      // 2 lines ← 已同步

21 读操作事务 readConcern

Read Concern

开启:enableMajorityReadConcern: true

// 从节点 1、2 锁定写入,模拟主从不一致
db.fsyncLock()
// 主节点
db.test.insert({ count: 3 })

db.test.find().readConcern("local")     // 可能脏读(若该写操作后被回滚)
db.test.find().readConcern("majority")  // 避免脏读

majority 近似 SQL 的 Read Committed

22 多文档事务

ACID 在 MongoDB 的实现

性质实现机制
Atomicity事务本身
ConsistencywriteConcern + readConcern
IsolationreadConcern
DurabilityJournal + Replication

约束:

  • 事务默认 60s 内必须完成。
  • 多文档事务中的读 必须走主节点

实验:事务隔离性

db.tx.drop();
db.tx.insertMany([{ x: 1 }, { x: 2 }]);

var session = db.getMongo().startSession();
session.startTransaction();
var coll = session.getDatabase('test').getCollection('tx');
coll.updateOne({ x: 2 }, { $set: { y: 1 } });

coll.find();    // 事务内:{x:1}, {x:2, y:1}
db.tx.find();   // 事务外:{x:1}, {x:2}             ← 隔离

session.commitTransaction();
db.tx.find();   // {x:1}, {x:2, y:1}

实验:可重复读(snapshot)

db.tx.drop();
db.tx.insertMany([{ x: 1 }, { x: 2 }]);

var session = db.getMongo().startSession();
session.startTransaction({
  readConcern: { level: "snapshot" },
  writeConcern: { w: "majority" }
});
var coll = session.getDatabase('test').getCollection('tx');
coll.findOne({ x: 1 });                  // 首次读

// 事务外的更新
db.tx.updateOne({ x: 1 }, { $set: { y: 1 } });

coll.findOne({ x: 1 });                  // 仍是首次的快照
session.abortTransaction();

实验:写冲突

// shell 1、2 同时开事务
session.startTransaction({
  readConcern: { level: "snapshot" },
  writeConcern: { w: "majority" }
});

// shell 1
coll.updateOne({ x: 1 }, { $set: { y: 1 } });   // ok

// shell 2
coll.updateOne({ x: 1 }, { $set: { y: 1 } });
// MongoServerError: WriteConflict — 须 retry 或 abort

23 Change Stream

类似数据库触发器,基于 oplog

对比传统触发器

维度触发器Change Stream
触发方式同步(事务保证)异步
触发位置数据库内客户端回调事件
触发次数一次每个订阅事件的客户端各一次
故障恢复事务回滚从断点重新触发

可追踪事件

  • insert / update / delete
  • drop / rename / dropDatabase
  • invalidatedrop / rename / dropDatabase 触发,并关闭 change stream

约束

  • 隔离级别 = 可重复读
  • 集群必须开启 majority readConcern
  • 集群无法满足 {w: "majority"} 时不会触发。
  • 可用聚合管道过滤事件。

25 分片集群机制及原理

进程角色

角色进程作用
路由mongos转发请求
配置mongod元数据 / 路由表
数据mongod实际存数据片段

数据分布策略

策略优点缺点典型场景
范围范围查询快数据可能倾斜自然有序的数据
哈希分布均匀范围查询差日志、物联网
Zone天然分区维护成本高按地区 / 业务隔离

26 分片集群设计

核心术语:

术语含义
shard key文档中作为分片依据的一个字段
doc文档
chunk一个 shard key 范围内的文档块
shard分片节点(一个 replica set)
cluster整个分片集群

片键设计三条

  1. 取值基数范围要
  2. 取值范围尽可能均匀
  3. 对主要查询要具有 定向能力

组合片键 例:{user_id: 1, time: 1}

27 分片集群搭建及扩容

# 1 配置域名解析
# 2 准备分片目录

# 3 创建第 1 个分片复制集 member1:27010 member3:27010 member5:27010
mongod --bind_ip_all --replSet shard1 --shardsvr --wiredTigerCacheSizeGB 1
# --shardsvr               标注为分片节点
# --wiredTigerCacheSizeGB  缓存大小

# 4 初始化分片复制集

# 5 创建 config server 复制集 member1:27019 member3:27019 member5:27019
mongod --bind_ip_all --replSet config --configsvr --wiredTigerCacheSizeGB 1
# --configsvr              标注为配置节点

# 6 初始化 config server 复制集

# 7 搭建 mongos
mongos --bind_ip_all --configdb config/member1:27019,member3:27019,member5:27019

# 连接 mongos 添加分片
sh.addShard("shard1/member1:27010,member3:27010,member5:27010")

# 8 创建分片表
sh.enableSharding("foo");
sh.shardCollection("foo.bar", {_id: "hashed"});
sh.status();

# 插入测试数据
use foo
for (var i = 0; i < 10000; i++) {
  db.bar.insert({ i: i })
}

# 9~11 创建并加入第 2 个分片复制集
sh.addShard("shard2/member2:27011,member4:27011,member6:27011")

28 监控最佳实践

工具:

  • MongoDB Ops Manager
  • Percona
  • 自研监控脚本

数据获取:

db.serverStatus()              // 从上次启动到现在
db.serverStatus().opcounters
db.isMaster()
mongostat                      // CLI 工具

29 备份与恢复

策略:

  • 延迟节点 —— 抗误删。
  • 全量 + Oplog 增量 —— oplog 幂等可重放。
  • mongodump --oplog 不遗漏 dump 期间的数据。
  • mongorestore --oplogReplay 配合恢复。

31 安全架构

db.createRole({
   role: "readWriteRole",
   privileges: [{
      resource: { db: "myDatabase", collection: "sample" },
      actions: [ "find", "insert", "update", "remove" ]
   }],
   roles: [{ role: 'read', db: 'sampledb' }]
})

db.createUser({
  user: 'sampleUser',
  pwd: 'pwd',
  roles: [{ role: 'readWriteRole', db: 'admin' }]
})

32 安全加固实践

mongod --noscripting   # 禁止脚本执行
mongod --auth          # 强制鉴权

33 索引机制(一)

核心概念速查:

概念含义
Index / Key / DataPage索引、键、数据页
Covered Query / FETCH完全由索引返回 / 需要回表取数据
IXSCAN / COLLSCAN索引扫描 / 集合扫描
Big O Notation时间复杂度
Query Shape查询形状(相同 shape 可复用执行计划)
Index Prefix索引前缀
Selectivity选择性 —— 过滤掉的比例越大越好

B-Tree vs B+Tree

维度B-TreeB+Tree
数据存放所有节点都可存 key + value仅叶子节点存 key + value
叶子链表叶子用指针串成有序链表,区间查询友好
适用内存索引、缓存磁盘索引(数据库、文件系统)

MongoDB / MySQL InnoDB 都用 B+Tree。

34 索引机制(二)

.explain(true) 关注字段:

字段含义
executionTimeMillis执行毫秒数
totalDocsExamined共扫描文档数
executionStages.docsExamined当前阶段扫描文档数
executionStages.inputStage.stage输入阶段类型

ESR 原则:组合索引顺序 = Equal(最前)→ Sort(中间)→ Range(最后)。

后台建索引:

db.member.createIndex({ city: 1 }, { background: true })

36 性能诊断工具

mongostat —— 实时运行状态:

指标关注阈值
dirty< 5%(脏页比例)
used缓存使用率
qrw排队请求数
con连接数

mongotop —— 集合维度压力。

mtools —— 日志分析。

41 应用场景及选型

优点:

  • 横向扩展 —— 数据 / 并发增长可自动扩容。
  • 灵活模型 —— 适合快速迭代、数据模型多变。
  • JSON 结构 —— 与微服务 / REST API 天然契合。

44 关系型数据库迁移

迁移到 MongoDB 的理由:

  • 高并发(数千 ~ 十万 ops),关系型不易扩展。
  • 快速迭代,关系型模式太严谨。
  • 灵活的 JSON 模式。
  • 大数据量需求。
  • 地理位置查询。
  • 多数据中心跨地域部署。

References

– EOF –