第一阶段:MongoDB 基础入门
1.什么是mongodb
MongoDB 是一款开源、跨平台的分布式文档型 NoSQL 数据库 ,由 MongoDB Inc. 公司开发,2007 年首次发布。文档型非关系数据库,用类似 JSON 的结构存储数据
- 分类:NoSQL(非关系型数据库),区别于 MySQL、Oracle 这类传统关系型数据库(RDBMS),适合互联网业务:用户信息、博客、评论、商品详情
- 核心存储单元:文档(Document),而非关系库的 "行"
- 数据格式:基于 BSON(二进制 JSON),兼容 JSON,支持数字、字符串、数组、嵌套对象、日期、二进制等复杂类型
2.对比mysql
| MongoDB 概念 | MySQL 对应概念 | 说明 |
|---|---|---|
| 数据库 (Database) | 数据库 | 独立数据仓库 |
| 集合 (Collection) | 数据表 (table) | 存放一批同类文档,无需提前定义表结构 |
| 文档 (Document) | 行 (row) | 一条完整数据,键值对结构 |
| 字段 (Field) | 列 (column) | 文档里的 key |
| _id | 主键 (primary key) | 文档唯一标识,自动生成 |
示例一条文档:
bash
{
"_id": ObjectId("60xxxxx"),
"name": "张三",
"age": 22,
"hobby": ["篮球","编程"],
"address": {"city":"长沙","district":"雨花区"}
}
3.数据格式bson与ObjectId原理
1、BSON:二进制JSON,是MongoDB数据存储格式,支持更多数据类型(日期、二进制、浮点、ObjectId等)。
2、_id 主键字段:
-
唯一标识文档,默认自动生成,不可重复
-
ObjectId结构:4字节时间戳+3字节机器ID+2字节进程ID+3字节自增计数器
-
自带时间属性,可通过_id快速提取文档创建时间
4.客户端工具
bash
# 带账号密码
mongosh -u admin -p 123456 --authenticationDatabase admin 127.0.0.1:27017
# 直接指定数据库
mongosh 127.0.0.1:27017/testdb
// 查看所有数据库
show dbs
show databases
// 切换/创建数据库(不存在则自动创建,插入数据后才持久化,隐式创建)
use testdb
// 查看当前所在库
db
// 查看当前库所有集合
show collections
show tables
// 手动创建集合(一般无需手动,插入数据自动生成)
db.createCollection("user")
// 删除集合
db.user.drop()
5.CURD语法
1. 插入文档 insertOne/insertMany
insertOne 插入单条
bash
db.user.insertOne({
name: "张三",
age: 20,
hobby: ["篮球", "游戏"],
addr: { city: "长沙" }
})
insertMany 批量插入多条
bash
db.user.insertMany([
{name:"李四",age:22},
{name:"王五",age:25}
])
2. 查询 find () /findOne ()
bash
// 所有数据
db.user.find()
// 格式化输出(美观)
db.user.find().pretty()
// 查询第一条匹配数据
db.user.findOne({name:"张三"})
// 查询所有匹配的数据
db.user.find({name:"张三"})
条件查询
bash
// 等于匹配
db.user.find({name:"张三"})
// 比较运算符
// $gt 大于、$gte大于等于、$lt小于、$lte小于等于、$ne不等于
db.user.find({age: {$gt: 20}})
// 多条件同时满足 and
db.user.find({age:{$gt:18}, name:"李四"})
// $or 或条件
db.user.find({
$or: [
{age: {$lt:20}},
{name:"王五"}
]
})
// in 匹配多个值
db.user.find({age: {$in: [20,22,25]}})
投影:只返回指定字段(1 显示,0 隐藏)
bash
// 只显示 name、age,隐藏 _id
db.user.find({age:{$gt:18}}, {name:1, age:1, _id:0})
分页、排序、数量
bash
// sort 排序:1升序 -1降序
db.user.find().sort({age: -1})
// limit 取前N条
db.user.find().limit(2)
// skip 跳过N条(分页)
db.user.find().skip(1).limit(2)
// 统计总条数
db.user.countDocuments({age: {$gt:18}})
3. 更新 updateOne /updateMany
更新操作必须搭配 $set,否则会覆盖整条文档
bash
// updateOne:只更新第一条匹配数据
db.user.updateOne(
{name:"张三"},
{$set: {age: 21, sex: "男"}} // 新增/修改字段
)
// updateMany:更新全部匹配数据
db.user.updateMany(
{age: {$gt:20}},
{$set: {status: "成年"}}
)
// $inc 数值自增
db.user.updateOne({name:"张三"}, {$inc:{age:1}})
// $push 数组追加元素
db.user.updateOne({name:"张三"}, {$push:{hobby:"跑步"}})
4. 删除 deleteOne /deleteMany
bash
// 删除匹配第一条
db.user.deleteOne({name:"张三"})
// 删除所有匹配数据
db.user.deleteMany({age: {$lt:18}})
// 删除集合全部数据
db.user.deleteMany({})
6.索引语法
bash
// 创建单字段索引 1升序 -1降序
db.user.createIndex({name:1})
// 复合索引
db.user.createIndex({name:1, age:-1})
// 唯一索引(字段不能重复)
db.user.createIndex({phone:1}, {unique:true})
// 查看集合所有索引
db.user.getIndexes()
// 删除索引
db.user.dropIndex("name_1")
7.聚合查询 aggregate(复杂统计)
管道操作符 $match 过滤、$group 分组、$sort、$limit
bash
// 统计不同年龄人数
db.user.aggregate([
// 过滤条件
{$match: {age: {$gte:18}}},
// 分组统计
{$group: {
_id: "$age", // 分组字段
total: {$sum: 1} // 计数
}},
// 排序
{$sort: {total:-1}}
])
8.权限 / 用户管理
bash
// 切换到admin库
use admin
// 创建管理员用户
db.createUser({
user: "admin",
pwd: "123456",
roles: ["root"]
})
// 创建test只有读权限对数据库testdb
db.createUser({
user: "test",
pwd: "123456",
roles: [{role:"read",db:"testdb"}]
})
// 查看所有用户
db.getUsers()
// 删除用户
db.dropUser("admin")
第二阶段: MongoDB进阶
1.MongoDB 索引底层原理
1. 索引是什么
索引是集合文档中指定字段的有序映射表 ,类似书本目录; 无索引时查询是全集合扫描(COLLSCAN) ,逐条对比文档,数据量大极慢; 有索引时走索引扫描(IXSCAN),直接通过索引定位文档。查看: db.users.getIndexes()
MongoDB 默认索引:_id 唯一索引,自动创建,不可删除。
2. 底层存储结构 B+Tree
- 内部节点:只存 key,不存储真实数据,用来路由查找;
- 所有真实数据 / 记录指针只存放在叶子节点;
- 叶子节点形成有序双向链表,范围查询极强。
3. 索引分类底层逻辑
(1)单字段索引
bash
db.user.createIndex({age:1})
(2)复合索引(最左前缀匹配原则: 查询条件必须优先使用索引最左侧的字段 ,索引才能正常生效; 一旦中间某个索引字段缺失、或者出现范围查询,该字段右侧所有索引列失效。)
bash
db.user.createIndex({name:1,age:1})
(3)唯一索引
bash
db.user.createIndex({phone:1},{unique:true})
(4)稀疏索引: 只存储存在该字段的文档,缺失字段不进入索引,节省空间。
bash
db.users.createIndex(
{email:1},
{sparse:true}
)
(5)文本索引(专门用来全文关键词检索,把字符串内容自动拆分成单词,快速搜索包含某个词语的文档:::: 倒排索引)
bash
db.articles.createIndex({ content: "text" })
(6) 地理索引
4. 索引读写代价
索引加速查询,但降低写入性能:
- 插入 / 更新 / 删除文档时,所有相关索引树必须同步修改;
- 索引越多,写操作(insert/update/delete)开销越大;
- 索引占用磁盘内存,索引过大无法放入内存时,查询性能暴跌。
5.索引执行计划(判断索引是否生效)
explain("executionStats") 分析执行计划,判断是否命中索引、扫描数据量、执行耗时
bash
// 查看查询执行详情
db.user.find({name:"张三",age:22}).explain("executionStats")
关键字段解读:
stage: COLLSCAN:全表扫描,无索引,需要建索引;stage: IXSCAN:命中索引,正常;
6.索引调优
1.优先将等值条件放在复合索引前面,范围条件放最后
2.遵循最左前缀原则,避免跳跃索引字段查询
3.索引字段上不要使用函数运算
4.控制索引数量,索引越多,写入压力越大。
5.清理无效 / 长期不使用的索引
第三阶段:分布式集群高可用(副本集+分片集群)
1.副本集 Replica Set
1. 副本集作用
- 故障自动转移,主节点宕机自动选新主,业务无感知;
- 数据多副本持久化,防止单盘 / 单节点丢失;
- 读写分离:主写、从读,分摊查询压力;
- 支持数据回滚、备份恢复。
2. 副本集角色(至少 3 节点标准架构)
(1)Primary 主节点
- 唯一接收写操作的节点;
- 所有写入记录 oplog(操作日志),同步给从节点;
- 一个副本集同一时间只有 1 个主。
(2)Secondary 从节点
- 异步同步主节点 oplog,保存完整数据副本;
- 不能写,默认不提供读;可开启 secondaryOk 实现读写分离;
- 主节点故障时参与投票选举新主。
(3)Arbiter 仲裁节点(可选)
- 不存数据,只参与投票;
- 场景:2 个数据节点时,加 1 个仲裁凑奇数,防止脑裂;
- 生产标准架构:3 个数据节点(1 主 2 从),无需仲裁。
3. 核心机制
3.1 Oplog 同步日志
- Primary 收到写请求,执行数据写入;
- 将本次写操作记录写入
oplog.rs; - Secondary 节点持续异步拉取主节点最新 Oplog;
- 从节点串行重放日志,保持数据和主节点同步;(从节点追不上主,需重新全量同步)
3.2 自动选举(高可用核心)
选举触发条件:主节点心跳失联(默认 10s) 规则:
- 总投票节点必须是奇数,避免平票脑裂;
- 候选节点必须拥有最新 oplog(数据最新)才能当选;
- 选举期间副本集只读,持续几秒,业务短暂不可写;
- 可配置优先级
priority控制节点优先当选主。
3.3 读写分离模式
bash
// mongosh 客户端设置读从节点
db.getMongo().setReadPref("secondaryPreferred")
读策略:
- primary:只读主(默认,强一致)
- primaryPreferred:优先主,主挂了读从
- secondary:只读从(适合报表、离线统计)
- secondaryPreferred:优先从,无从再读主
4. 副本集故障演示
- 主节点宕机 → 2 个从节点检测心跳丢失;
- 发起选举,数据最新从节点成为新主;
- 应用自动重连新主,业务恢复写入;
- 旧主重启后自动变成从,同步缺失 oplog。
2.分片集群 Sharded Cluster(分布式扩容)
当单副本集数据量 TB 级、单库性能瓶颈,使用分片集群水平拆分数据。
1. 分片集群四大组件
1)Shard 分片节点
- 每一个 Shard 都是独立副本集,自带高可用;
- 数据按分片键拆分,分散存储在不同 shard;
- 示例:shard1 (1 主 2 从)、shard2 (1 主 2 从)、shard3 (1 主 2 从)
2)mongos 路由节点
- 无持久化数据,内存进程;
- 接收客户端所有 CRUD 请求;
- 根据分片键路由请求到对应 shard;
- 聚合、跨分片查询由 mongos 汇总结果;
- 生产部署多个 mongos 做负载均衡,防止单点故障。
3)Config Server 配置服务器
- 存储集群元数据:分片信息、分片键、集合路由规则、分片均衡器状态;
- 必须部署副本集(3 节点),保证元数据高可用;
- mongos 启动时拉取配置元数据,缓存本地。
4)Balancer 均衡器(后台进程)
运行在 config 服务器,自动监控分片数据块(chunk)分布; 当分片数据差距过大,自动迁移 chunk 到压力小的分片,平衡数据。
2. 分片核心概念:分片键 Shard Key
(1)Chunk 数据块
MongoDB 将集合数据切分成固定大小 chunk(默认 64MB),每个 chunk 只属于一个 shard。
(2)分片键作用
指定集合内 1 个 / 多个字段作为拆分依据,决定数据路由规则。 两种分片策略:
- 范围分片:按字段数值区间划分 chunk 例:user_id 1~10000 → shard1;10001~20000 → shard2 适合范围查询,缺点:热点写入(自增 ID 全部写入同一个分片)
- 哈希分片:对分片键哈希取模,均匀打散数据到所有分片 解决自增 ID 热点写入,但范围查询性能差
(3) 分片键设计禁忌
- 低区分度字段(性别、状态)不能做分片键;
- 频繁更新分片键字段会触发 chunk 迁移,性能暴跌;
- 分片键一旦选定,无法修改,设计前必须评估业务
3. 分片集群读写流程
- 客户端连接 mongos;
- mongos 向 config 拉取路由元数据;
- 根据查询条件 + 分片键判断数据在哪个 shard;
- 单分片查询:直接下发对应 shard,性能高;
- 跨分片查询:mongos 向所有 shard 分发请求,再聚合结果(性能差);
- 插入 / 更新 / 删除文档时:路由到对应 shard 副本集主节点;
- balancer 后台自动均衡 chunk。
4. 高可用保障机制
- 每个 shard 是副本集:单分片节点宕机自动选主,数据不丢;
- config 3 节点副本集:元数据多副本,元数据不丢失;
- 多 mongos 部署:前端加负载均衡(Nginx/LVS),单个 mongos 挂了不影响业务;
- chunk 迁移热迁移:迁移过程不阻塞读写,平衡数据无停机。
3.常见常用运维命令
1.副本集操作
bash
// 查看副本集状态
rs.status()
// 查看配置
rs.conf()
// 手动降级主节点(主动切换主)
rs.stepDown()
2.分片集群操作(mongos 中执行)
bash
// 查看分片列表
sh.status()
// 开启集合分片
sh.shardCollection("test.user", {shardKey: {userId: "hashed"}})
// 手动触发均衡
sh.startBalancer()
// 查看chunk分布
db.user.getShardDistribution()
第四阶段:运维安全、性能调优、项目实战
一、数据备份与恢复
1.逻辑备份
bash
# 指定IP端口、账号密码、指定数据库
mongodump -h 127.0.0.1:27017 -u admin -p 123456
--authenticationDatabase admin -d testdb -o ./backup_test
#--gzip:压缩备份
# 只备份单个集合
mongodump -d testdb -c user -o ./backup_user
2.逻辑恢复
bash
# 恢复指定数据库,带认证
mongorestore -h 127.0.0.1:27017 -u admin -p 123456 --authenticationDatabase admin -d testdb ./backup_test/testdb
# 恢复压缩包
mongorestore --host 127.0.0.1 --port 27017 -u admin -p 123456 --authenticationDatabase admin --gzip ./backup_gz
3.物理备份
直接复制数据目录 /data/db 到备份盘
依赖 LVM 快照、云磁盘快照(阿里云 / 腾讯云磁盘快照)
二、慢查询监控
开启Profiler慢日志、分析慢查询、定位低效语句、优化SQL。
三、存储引擎与参数调优
WiredTiger默认存储引擎原理、缓存配置、内存优化、磁盘IO优化。