MongoDB 知识点全览
一、核心概念与基础
-
文档数据模型
- BSON 格式(二进制 JSON)
- 嵌套文档与数组支持
- 动态 Schema 设计
javascript{ _id: ObjectId("5f9d1a2e3c8f4b4d6e7f8a9b"), name: "产品A", tags: ["电子", "促销"], details: { price: 99.99, stock: 200 } }
-
安装与配置
- 多平台安装(Linux/Windows/macOS)
- 配置文件参数详解(
mongod.conf
)
一、存储配置(Storage)
1. storage.dbPath
-
作用:数据库文件存储路径
-
默认值 :
/data/db
(Linux)或\data\db
(Windows) -
示例 :
yamlstorage: dbPath: /var/lib/mongodb
2. storage.engine
-
作用:存储引擎选择
-
可选值 :
wiredTiger
(默认,支持压缩和事务)inMemory
(纯内存引擎,企业版专用)
-
示例 :
yamlstorage: engine: wiredTiger
3. storage.wiredTiger
- 子参数 :
-
engineConfig.cacheSizeGB
:WiredTiger 缓存大小(建议为物理内存的50%-80%)yamlwiredTiger: engineConfig: cacheSizeGB: 4
-
collectionConfig.blockCompressor
:集合数据压缩算法snappy
(默认,压缩速度快)zlib
(压缩率高但CPU消耗大)
yamlwiredTiger: collectionConfig: blockCompressor: zlib
-
二、网络配置(Net)
1. net.port
-
作用:MongoDB 监听端口
-
默认值 :
27017
-
示例 :
yamlnet: port: 27018
2. net.bindIp
-
作用:绑定监听的IP地址
-
特殊值 :
0.0.0.0
(监听所有网络接口)127.0.0.1
(仅本地访问)
-
生产建议 :
yamlnet: bindIp: 192.168.1.100,127.0.0.1
3. net.tls
-
作用:启用TLS/SSL加密
-
关键参数 :
yamlnet: tls: mode: requireTLS certificateKeyFile: /etc/ssl/mongodb.pem CAFile: /etc/ssl/ca.pem
三、安全配置(Security)
1. security.authorization
-
作用:启用访问控制
-
可选值 :
enabled
(必须配置用户权限)disabled
(无鉴权,仅测试环境使用)
-
示例 :
yamlsecurity: authorization: enabled
2. security.keyFile
-
作用:副本集/分片集群节点间认证密钥文件
-
生成方式:
bashopenssl rand -base64 756 > /path/to/keyfile chmod 400 /path/to/keyfile
-
配置示例:
yamlsecurity: keyFile: /etc/mongodb/keyfile
四、副本集配置(Replication)
1. replication.replSetName
-
作用:副本集名称(集群内所有节点必须相同)
-
示例 :
yamlreplication: replSetName: rs0
2. replication.oplogSizeMB
-
作用:设置oplog日志大小(建议为磁盘空间的5%-50%)
-
调整原则 :
yamlreplication: oplogSizeMB: 20480 # 20GB
五、分片配置(Sharding)
1. 配置服务器角色
yaml
sharding:
clusterRole: configsvr # 配置服务器节点
2. 分片节点角色
yaml
sharding:
clusterRole: shardsvr # 分片存储节点
六、日志配置(SystemLog)
1. systemLog.destination
-
作用:日志输出方式
-
可选值 :
file
(输出到文件)syslog
(系统日志服务)
-
示例 :
yamlsystemLog: destination: file path: /var/log/mongodb/mongod.log logAppend: true # 追加模式
2. systemLog.verbosity
-
作用:日志详细级别(0-5,数值越大越详细)
-
生产建议 :
yamlsystemLog: verbosity: 1 # 一般生产环境设为1
七、性能调优参数
1. 连接池配置
yaml
net:
maxIncomingConnections: 1000 # 最大连接数
wireObjectCheck: false # 关闭BSON验证提升性能
2. 写关注配置
yaml
writeConcern:
w: "majority" # 默认写确认级别
journal:
enabled: true # 启用日志持久化
配置文件示例(生产环境)
yaml
# mongod.conf
systemLog:
destination: file #日志输出方式
path: /var/log/mongodb/mongod.log #日志输出文件路径
logAppend: true # 追加模式
storage:
dbPath: /data/mongodb #数据库文件存储路径
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 8 #WiredTiger 缓存大小(建议为物理内存的50%-80%)
collectionConfig:
blockCompressor: zlib #集合数据压缩算法(zlib压缩率高但CPU消耗大)
net:
port: 27017 #MongoDB 监听端口(默认值:27017)
bindIp: 192.168.1.100,127.0.0.1 #绑定监听的IP地址
tls: #启用TLS/SSL加密
mode: requireTLS
certificateKeyFile: /etc/ssl/mongodb.pem
security:
authorization: enabled #启用访问控制 (enabled 必须配置用户权限)
keyFile: /etc/mongodb/keyfile #副本集/分片集群节点间认证密钥文件
replication:
replSetName: rs-prod #副本集名称(集群内所有节点必须相同)
oplogSizeMB: 20480 #设置oplog日志大小(建议为磁盘空间的5%-50%)
sharding:
clusterRole: shardsvr # 分片存储节点
关键注意事项
-
修改配置后必须重启服务生效:
bashsudo systemctl restart mongod
-
动态参数调整(无需重启):
javascriptdb.adminCommand({ setParameter: 1, wiredTigerConcurrentReadTransactions: 128 })
-
配置文件验证:
bashmongod --config /etc/mongod.conf --fork
建议通过 MongoDB 官方配置文档(MongoDB Configuration File Options)查询完整参数列表。
- CRUD 操作
- 插入:
insertOne()
,insertMany()
- 查询:
find()
操作符($eq
,$in
,$regex
) - 更新:
updateOne()
修改符($set
,$inc
,$push
) - 删除:
deleteMany()
- 插入:
二、索引与查询优化
- 索引类型
- 单字段索引
- 复合索引(查询排序优化)
- 多键索引(数组字段)
- 地理空间索引(
2dsphere
)
一、2dsphere 索引核心特性
-
支持数据类型:
Point
(坐标点)LineString
(路径线)Polygon
(多边形区域)MultiPoint
/MultiLineString
/MultiPolygon
(复合图形)
-
适用场景:
- 查找附近地点(如 "附近的餐厅")
- 判断点是否在区域内(地理围栏)
- 计算两点间距离
- 路径规划(交叉分析)
二、索引创建方法
1. 数据格式要求
必须使用 GeoJSON 格式 存储地理数据:
javascript
// 点坐标示例
{
_id: 1,
name: "阿里云杭州园区",
location: {
type: "Point",
coordinates: [120.12345, 30.23456] // [经度, 纬度]
}
}
// 多边形区域示例
{
_id: 2,
area: "西湖景区",
boundary: {
type: "Polygon",
coordinates: [[
[120.1, 30.2],
[120.2, 30.2],
[120.2, 30.1],
[120.1, 30.1],
[120.1, 30.2]
]]
}
}
2. 创建索引命令
javascript
// 单字段索引
db.places.createIndex({ location: "2dsphere" });
// 复合索引(结合业务属性)
db.places.createIndex({
location: "2dsphere",
category: 1
});
三、地理空间查询操作
1. 附近地点搜索 ($near
)
javascript
// 查找距离坐标 [120,30] 最近的10个点
db.places.find({
location: {
$near: {
$geometry: {
type: "Point",
coordinates: [120.0, 30.0]
},
$maxDistance: 5000 // 最大距离5公里
}
}
}).limit(10);
2. 区域包含判断 ($geoWithin
)
javascript
// 查找位于多边形区域内的所有点
db.places.find({
location: {
$geoWithin: {
$geometry: {
type: "Polygon",
coordinates: [[
[120.1,30.1], [120.2,30.1],
[120.2,30.0], [120.1,30.0],
[120.1,30.1]
]]
}
}
}
});
3. 几何相交判断 ($geoIntersects
)
javascript
// 查找与某条路径相交的区域
db.areas.find({
boundary: {
$geoIntersects: {
$geometry: {
type: "LineString",
coordinates: [
[120.0,30.0], [121.0,31.0]
]
}
}
}
});
四、性能优化建议
-
索引覆盖原则:
- 优先在查询条件字段上创建复合索引
javascriptdb.places.createIndex({ location: "2dsphere", status: 1 });
-
结果集限制:
- 结合
limit()
控制返回数量 - 使用
$maxDistance
限制搜索半径
- 结合
-
计算距离优化:
javascript// 返回距离并排序 db.places.aggregate([ { $geoNear: { near: { type: "Point", coordinates: [120, 30] }, distanceField: "dist.calculated", maxDistance: 5000, spherical: true } }, { $sort: { "dist.calculated": 1 } } ]);
五、常见问题排查
-
坐标顺序错误:
- 必须为
[longitude, latitude]
(经度在前)
- 必须为
-
无效几何类型:
- 确保
type
字段拼写正确(如 "Point" 首字母大写)
- 确保
-
索引未命中:
javascript// 验证查询是否使用索引 db.places.find({...}).explain("executionStats");
六、应用场景示例
-
物流配送系统:
- 查找配送点 5 公里内的骑手
- 规划最优配送路径
-
社交应用:
- 显示用户周围 1 公里的动态
- 地理围栏签到功能
-
IoT 设备监控:
- 追踪设备移动轨迹
- 判断设备是否进入禁入区域
通过合理使用 2dsphere
索引,可显著提升地理空间查询性能(速度提升 10-100 倍)。建议结合 MongoDB Compass 可视化工具进行地理数据验证。
- 文本索引(中文分词需配置)
一、文本索引核心机制
1. 默认行为
- 分词规则:按空格/标点切分单词(英文友好)
- 停用词过滤:自动忽略 "a", "the", "is" 等常见词
- 大小写不敏感:搜索 "apple" 可匹配 "Apple"
2. 中文分词痛点
- 无自然分隔符:中文句子需通过分词算法处理(如 "我爱编程" → ["我", "爱", "编程"])
- 语义复杂性:需处理多音字、歧义词、新词等
二、中文分词配置方案
方案 1:使用第三方分词插件
-
推荐工具 :
mongodb-text-search-extra
+jieba
(结巴分词) -
配置步骤 :
bash# 安装 Python 依赖 pip install jieba pymongo # 启动 MongoDB 插件服务 docker run -d --name mongo-text-zh \ -v /path/to/jieba:/plugins \ mongo:5.0 --pluginDirectory /plugins
方案 2:预分词存储
-
实现方式 :在写入数据时预先分词
javascript// 文档存储结构 { _id: 1, content: "MongoDB支持中文全文搜索", keywords: ["mongodb", "支持", "中文", "全文", "搜索"] } // 创建文本索引 db.articles.createIndex({ keywords: "text" });
三、中文全文搜索实现
1. 创建支持中文的文本索引
javascript
// 自定义分词器(需插件支持)
db.adminCommand({
setParameter: 1,
textSearchEnabled: true,
textSearchDefaultLanguage: "chinese"
});
// 创建索引
db.news.createIndex(
{ title: "text", content: "text" },
{
default_language: "chinese",
weights: { title: 10, content: 5 } // 标题权重更高
}
);
2. 执行中文全文搜索
javascript
// 基础搜索
db.news.find({
$text: {
$search: "数据库性能优化",
$language: "chinese"
}
});
// 按相关性排序
db.news.find(
{ $text: { $search: "云计算" } },
{ score: { $meta: "textScore" } }
).sort({ score: { $meta: "textScore" } });
四、性能优化与限制
1. 索引优化技巧
-
复合索引策略:结合业务字段过滤
javascriptdb.products.createIndex({ name: "text", category: 1, price: 1 });
-
索引内存控制 :监控
db.collection.stats()
中的索引大小
2. 中文分词限制
- 词库更新:需定期更新第三方分词器的词库(如添加新词 "元宇宙")
- 长文本处理:建议拆分为段落 + 分片集群
- 模糊匹配 :需结合
$regex
处理未登录词
五、应用场景示例
案例 1:电商商品搜索
javascript
// 创建索引
db.products.createIndex({
title: "text",
description: "text"
}, {
default_language: "chinese",
weights: { title: 15, description: 5 }
});
// 搜索 "防水蓝牙耳机"
db.products.find({
$text: {
$search: "防水 蓝牙 耳机",
$language: "chinese"
}
});
案例 2:新闻舆情分析
javascript
// 聚合搜索热词
db.news.aggregate([
{ $match: { $text: { $search: "人工智能" } } },
{ $unwind: "$keywords" },
{ $group: { _id: "$keywords", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
]);
六、注意事项
-
版本兼容性:
- MongoDB 4.4+ 对中文支持更好
- 第三方插件需匹配 MongoDB 主版本
-
停用词管理:
javascript// 自定义停用词表 db.adminCommand({ setParameter: 1, textSearchStopWordFiles: "/data/mongodb/cn_stopwords.txt" });
-
云服务支持:
- MongoDB Atlas 已内置多语言分词(需企业版)
javascript
// 创建 TTL 索引(自动过期)
db.logs.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 3600 })
-
查询计划分析
explain("executionStats")
解读- 索引覆盖扫描(Covered Query)
- 强制索引使用:
hint()
javascriptdb.orders.find({ status: "A" }).hint({ status: 1, date: -1 })
三、聚合框架
- 聚合管道
- 阶段操作符:
$match
(过滤)$group
(分组统计)$project
(字段重塑)
- 阶段操作符:
$project
是 MongoDB 聚合管道中的核心操作符,用于 重塑文档结构(类似 SQL 的 SELECT 字段选择),可执行以下关键操作:
一、核心功能解析
1. 字段显式包含/排除
javascript
// 仅保留特定字段(_id默认保留)
db.orders.aggregate([
{
$project: {
_id: 0, // 排除_id
orderId: 1, // 包含orderId
total: 1 // 包含total
}
}
])
2. 字段重命名
javascript
// 修改字段名
{
$project: {
"客户ID": "$userId", // 原字段userId → 新名"客户ID"
"金额": "$amount"
}
}
3. 创建计算字段
javascript
// 生成新字段
{
$project: {
totalWithTax: {
$add: [ "$total", { $multiply: [ "$total", 0.1 ] } ] // 总价+10%税
}
}
}
二、高级重塑技巧
1. 嵌套结构展开
javascript
// 提升嵌套字段到顶层
{
$project: {
"userName": "$userInfo.name",
"userAge": "$userInfo.age"
}
}
2. 数组首元素提取
javascript
// 获取数组第一个元素
{
$project: {
firstItem: { $arrayElemAt: [ "$items", 0 ] }
}
}
3. 条件字段赋值
javascript
// 根据条件设置字段值
{
$project: {
discountType: {
$cond: {
if: { $gte: [ "$total", 1000 ] },
then: "VIP",
else: "普通"
}
}
}
}
三、使用场景示例
场景 1:数据脱敏
javascript
// 隐藏敏感信息
db.users.aggregate([
{
$project: {
_id: 0,
name: 1,
maskedPhone: {
$concat: [
{ $substrCP: [ "$phone", 0, 3 ] },
"****",
{ $substrCP: [ "$phone", 7, 4 ] }
]
}
}
}
])
场景 2:API 数据格式化
javascript
// 转换字段结构适配前端
{
$project: {
id: { $toString: "$_id" }, // ObjectId转字符串
title: 1,
meta: {
createdAt: 1,
updatedAt: 1
}
}
}
四、性能优化指南
-
管道顺序优化
尽早使用
$project
减少后续阶段处理的数据量:javascript// 推荐写法(先过滤后重塑) [ { $match: { status: "A" } }, { $project: { keyFields: 1 } }, { $group: ... } ]
-
避免重复计算
对频繁使用的计算字段建立索引或物化视图。
-
内存控制
监控
allowDiskUse
参数防止大数据集内存溢出:javascriptdb.collection.aggregate(pipeline, { allowDiskUse: true })
五、与 $addFields 的区别
特性 | $project | $addFields |
---|---|---|
字段保留 | 必须显式声明要保留的字段 | 保留所有原字段,仅添加新字段 |
典型场景 | 精简输出结构 | 补充计算字段而不改变原有结构 |
性能影响 | 减少数据传输量 | 可能增加文档大小 |
六、注意事项
-
字段覆盖风险
新字段名不能与原字段名冲突(除非故意覆盖)。
-
类型转换陷阱
使用
$convert
明确处理类型转换:javascript{ $project: { totalString: { $convert: { input: "$total", to: "string" } } } }
-
嵌套层级限制
MongoDB 文档嵌套深度最大为 100 层。通过合理使用
$project
,可使聚合结果体积减少 30%-70%(根据字段选择情况),显著提升查询性能。建议结合$unset
阶段进行字段清理。
$lookup
(多表关联)$bucket
(分桶统计)
$bucket
是 MongoDB 聚合框架中用于 数据分箱统计 的核心操作符,可将数值型字段按指定区间分组统计(类似 SQL 的 CASE WHEN + GROUP BY 组合)。
一、核心机制解析
1. 基本语法结构
javascript
{
$bucket: {
groupBy: "<expression>", // 分桶依据字段
boundaries: [<lower1>, <lower2>, ...], // 分桶边界值
default: "<literal>", // 未匹配数据的默认桶名
output: { // 统计输出
"<output1>": { <accumulator> },
...
}
}
}
2. 边界值规则
- 必须严格递增 :例如
[0, 100, 200]
- 左闭右开区间 :
0 ≤ value < 100
进入第一个桶 - 类型一致性 :边界值必须与
groupBy
字段类型一致
二、典型使用场景
场景 1:年龄分段统计
javascript
// 按年龄分组统计用户数
db.users.aggregate([
{
$bucket: {
groupBy: "$age",
boundaries: [0, 18, 30, 50, 100],
default: "其他",
output: {
count: { $sum: 1 },
avgSalary: { $avg: "$salary" }
}
}
}
])
// 输出示例
[
{ "_id": 0, "count": 150, "avgSalary": 3000 },
{ "_id": 18, "count": 500, "avgSalary": 8500 },
{ "_id": 30, "count": 1200, "avgSalary": 15000 },
{ "_id": "其他", "count": 50, "avgSalary": null }
]
场景 2:价格区间商品分布
javascript
// 统计商品价格分布
db.products.aggregate([
{
$bucket: {
groupBy: "$price",
boundaries: [0, 100, 500, 1000],
output: {
total: { $sum: 1 },
topItems: { $push: "$name" }
}
}
}
])
三、高级分桶技巧
1. 动态边界计算
javascript
// 自动计算价格分位点
db.products.aggregate([
{
$bucketAuto: {
groupBy: "$price",
buckets: 5, // 分成5个等宽区间
output: {
count: { $sum: 1 },
minPrice: { $min: "$price" },
maxPrice: { $max: "$price" }
}
}
}
])
2. 复合分桶条件
javascript
// 组合多个字段分桶
db.orders.aggregate([
{
$bucket: {
groupBy: {
$concat: [
{ $toString: { $year: "$orderDate" } },
"-",
{ $toString: { $month: "$orderDate" } }
]
},
boundaries: ["2023-1", "2023-7", "2024-1"],
output: {
totalSales: { $sum: "$amount" }
}
}
}
])
四、与 $group 对比
特性 | $bucket |
$group |
---|---|---|
分组方式 | 预定义固定区间 | 自由定义分组键 |
边界控制 | 明确指定边界值 | 依赖数据分布 |
适用场景 | 等距/自定义区间统计 | 离散值聚合统计 |
输出结构 | 自动生成区间标识 | 需手动设置分组键 |
五、性能优化建议
-
前置过滤数据
在
$bucket
前使用$match
减少处理量:javascript[ { $match: { category: "电子产品" } }, { $bucket: ... } ]
-
索引利用
对
groupBy
字段建立索引加速分桶:javascriptdb.products.createIndex({ price: 1 })
-
控制输出量级
合理设置
boundaries
数量避免内存溢出:javascript{ boundaries: [0, 100, 200, 300, 400, 500] } // 6个桶
六、常见问题处理
问题 1:边界值未覆盖所有数据
-
现象 :出现大量
default
桶数据 -
解决方案 :
javascript// 扩展边界值范围 boundaries: [0, 100, 500, 1000, Number.MAX_SAFE_INTEGER]
问题 2:浮点数精度问题
-
现象 :
99.999999
未进入[100, 200)
桶 -
解决方案 :
javascriptgroupBy: { $round: ["$value", 2] } // 保留两位小数
七、实战案例:电商用户消费分析
javascript
// 分析用户年度消费等级
db.orders.aggregate([
{
$group: {
_id: "$userId",
totalSpent: { $sum: "$amount" }
}
},
{
$bucket: {
groupBy: "$totalSpent",
boundaries: [0, 1000, 5000, 10000],
output: {
users: { $push: "$_id" },
count: { $sum: 1 },
avgSpent: { $avg: "$totalSpent" }
}
}
}
])
// 输出结果示例
[
{
"_id": 0,
"count": 1500,
"avgSpent": 650,
"users": [ "U1001", "U1002", ... ]
},
{
"_id": 1000,
"count": 800,
"avgSpent": 2800,
"users": [ "U2001", "U2002", ... ]
}
]
通过合理使用 $bucket
,可使区间统计效率提升 3-5 倍(对比手动 $group
实现)。建议结合 $sortByCount
进行结果排序展示。
javascript
db.sales.aggregate([
{ $match: { year: 2023 } },
{ $group: { _id: "$product", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } }
])
-
MapReduce
- 自定义 JavaScript 函数
- 适用大规模数据分析
javascriptdb.orders.mapReduce( function() { emit(this.product, this.quantity); }, function(key, values) { return Array.sum(values); }, { out: "product_totals" } )
四、复制集(Replica Set)
- 架构原理
- 主节点(Primary)与从节点(Secondary)
- 选举机制(Raft 协议变种)
MongoDB 副本集的选举机制基于 Raft 共识算法 的改进版本,主要目的是在节点故障时快速选出新主节点(Primary),确保集群高可用。以下是其核心原理和实现细节:
一、选举触发条件
- 主节点失联(心跳超时,默认 10 秒)
- 手动触发选举 (
rs.stepDown()
) - 节点优先级变更(高优先级节点加入)
二、选举流程详解
步骤 1:节点角色转换
- 主节点离线 → 所有从节点(Secondary)转为 候选人(Candidate)
- 候选人发起投票请求 给其他节点
步骤 2:投票规则
-
投票权分配:
javascript// 查看节点投票权 rs.conf().members[0].votes // 1 表示有投票权
-
多数派原则(Majority):
- 3 节点集群 → 至少 2 票
- 5 节点集群 → 至少 3 票
-
仲裁节点(Arbiter):
-
仅参与投票,不存储数据
-
配置示例:
javascriptrs.addArb("mongo3:27017")
-
步骤 3:选举决胜条件
-
数据最新性(Optime 比较):
- 只有拥有最新 oplog 的节点可当选
javascript// 查看节点数据同步状态 rs.printReplicationInfo()
-
优先级权重(手动配置):
javascriptcfg.members[0].priority = 2 // 更高优先级 rs.reconfig(cfg)
步骤 4:新主节点生效
- 当选节点升级为主节点
- 其他节点转为从节点并开始同步数据
三、与标准 Raft 的关键差异
特性 | MongoDB 选举 | 标准 Raft |
---|---|---|
数据一致性检查 | 强制要求候选人有最新 oplog | 仅需获得多数票 |
优先级干预 | 支持优先级强制选举(priority) | 无优先级概念 |
投票成员 | 可配置无数据节点(Arbiter) | 所有节点必须存储数据 |
心跳机制 | 默认 2 秒一次心跳 | 心跳间隔可自由配置 |
四、选举超时控制
-
配置参数:
yaml# mongod.conf replication: electionTimeoutMillis: 10000 # 默认10秒 heartbeatIntervalMillis: 2000 # 心跳间隔2秒
-
网络分区处理:
- 若剩余节点不足多数派 → 集群进入 只读模式
- 恢复网络后自动重新选举
五、选举性能优化
-
合理设置节点优先级:
javascript// 让上海机房节点优先成为主节点 cfg.members[0].priority = 3 cfg.members[1].priority = 2 cfg.members[2].priority = 1 rs.reconfig(cfg)
-
跨机房部署建议:
- 每个机房部署投票节点形成多数派
- 使用
tag
控制数据同步方向
javascriptcfg.members[0].tags = { "dc": "shanghai" }
六、选举事件监控
1. 日志监控
bash
# 查看选举日志
grep "election" /var/log/mongodb/mongod.log
2. 命令监控
javascript
// 查看选举统计
db.serverStatus().repl.electionStats
// 实时监控
rs.status().members.forEach(m => {
printjson({
name: m.name,
stateStr: m.stateStr,
optime: m.optime
})
})
七、故障模拟测试
测试 1:主节点宕机
bash
# 在主节点执行强制关机
db.adminCommand({ shutdown: 1 })
# 观察选举日志(预期10秒内完成切换)
测试 2:网络分区模拟
bash
# 使用 iptables 阻断主节点网络
iptables -A INPUT -p tcp --dport 27017 -j DROP
# 验证从节点是否升主
八、生产环境建议
- 最小集群规模:3 节点(1 Primary + 2 Secondary)
- 避免过多 Arbiter:Arbiter 数量 ≤ 数据节点数量
- 跨机房部署:至少 2 个机房部署投票节点
- 定期演练:每季度执行故障转移测试
通过合理配置,MongoDB 副本集选举可在 10-30 秒内 完成故障转移(具体时间取决于 electionTimeoutMillis
设置)。建议结合 MongoDB Atlas 的自动故障转移功能(SLA 保证 99.995% 可用性)提升生产环境可靠性。
- 数据同步流程(Oplog 重放)
-
部署与管理
bash# 初始化副本集 rs.initiate({ _id: "rs0", members: [ { _id: 0, host: "node1:27017" }, { _id: 1, host: "node2:27017" }, { _id: 2, host: "node3:27017", arbiterOnly: true } ] })
- 节点状态监控:
rs.status()
- 强制重新选举:
rs.stepDown()
- 节点状态监控:
五、分片集群(Sharding)
-
核心组件
- 配置服务器(Config Server)
- 路由节点(Mongos)
- 分片节点(Shard)
-
分片键策略
- 范围分片 vs 哈希分片
- 分片键选择原则(基数、写分布、查询模式)
javascriptsh.shardCollection("mydb.users", { "user_id": "hashed" })
-
数据均衡管理
- 手动拆分 Chunk:
splitAt()
- 迁移控制:
moveChunk()
- 均衡器状态监控:
sh.isBalancerRunning()
- 手动拆分 Chunk:
六、存储引擎
-
WiredTiger 特性
- 文档级并发控制(MVCC)
- 压缩算法(Snappy/Zlib)
- 检查点机制(数据持久化)
-
内存配置优化
bash# 调整 WiredTiger 缓存大小 mongod --wiredTigerCacheSizeGB 4
七、事务管理
-
多文档事务
- 会话(Session)控制
- 事务隔离级别(读已提交)
javascriptconst session = db.getMongo().startSession(); session.startTransaction(); try { db.accounts.update({ _id: "A" }, { $inc: { balance: -100 } }, { session }); session.commitTransaction(); } catch (e) { session.abortTransaction(); }
-
限制与优化
- 事务超时(默认 60 秒)
- 避免跨分片事务性能损耗
MongoDB 在 分片集群 中支持跨分片的多文档事务(从 4.2 版本开始),其实现基于改进的两阶段提交协议(2PC)。以下是分片集群事务的完整实现机制和使用指南:
一、分片集群事务核心原理
1. 事务协调流程
- 事务管理器 :MongoDB 自动选举一个协调节点(通常为
mongos
) - 两阶段提交 :
- 准备阶段:向所有涉及的分片发送预提交请求
- 提交阶段:收到全部分片确认后提交事务
2. 关键限制
- 超时时间:默认 60 秒(可配置)
- 数据大小:单个事务操作不超过 16MB
- 隔离级别:快照隔离(Snapshot Isolation)
二、分片集群事务实现步骤
步骤 1:启用分片集群事务支持
javascript
// 确保所有节点为 4.2+ 版本
sh.enableSharding("mydb")
sh.shardCollection("mydb.orders", { orderId: "hashed" })
// 配置事务超时(可选)
db.adminCommand({
setParameter: 1,
transactionLifetimeLimitSeconds: 120
})
步骤 2:事务操作代码示例
javascript
const session = db.getMongo().startSession();
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" }
});
try {
// 跨分片操作示例
const orders = session.getDatabase("mydb").orders;
const inventory = session.getDatabase("mydb").inventory;
orders.insertOne({ orderId: 1001, items: ["itemA"], status: "pending" });
inventory.updateOne(
{ sku: "itemA" },
{ $inc: { stock: -1 } }
);
session.commitTransaction();
} catch (error) {
session.abortTransaction();
throw error;
}
三、性能优化策略
1. 分片键设计优化
-
减少跨分片操作 :通过合理分片键使事务操作集中在同一分片
javascript// 用户维度分片键(同一用户操作在相同分片) sh.shardCollection("mydb.orders", { userId: 1, orderId: 1 })
2. 读写关注配置
javascript
// 读关注设置
session.startTransaction({
readConcern: { level: "local" } // 低延迟读
});
// 写关注设置
{ writeConcern: { w: 1 } } // 快速确认
3. 重试逻辑实现
javascript
function runTransactionWithRetry(txnFunc, maxRetries = 3) {
let attempt = 0;
while (attempt <= maxRetries) {
try {
return txnFunc();
} catch (error) {
if (error.errorLabels?.includes("TransientTransactionError")) {
attempt++;
continue;
}
throw error;
}
}
}
四、监控与故障排查
1. 事务监控命令
javascript
// 查看当前活动事务
db.currentOp({ "lsid": { $exists: true } })
// 事务统计信息
db.serverStatus().transactions
2. 关键性能指标
- 事务提交延迟 :
transaction_commit_latency
- 冲突回滚率 :
transaction_aborts_due_to_conflict
- 锁等待时间 :
lockAcquisitionTime
五、生产环境注意事项
1. 硬件资源配置
- 内存:确保每个分片有足够内存缓存频繁访问的数据
- 网络:分片间需低延迟(建议 ≤ 10ms)
2. 事务限制规避
- Chunk 迁移:避免在事务执行期间迁移 chunk
- DDL 操作 :禁止在事务中执行
createIndex
等 DDL 命令
3. 备份策略
bash
# 事务一致性备份
mongodump --uri "mongodb://shard1:27017" \
--oplog \
--authenticationDatabase admin
六、与副本集事务的差异
特性 | 分片集群事务 | 副本集事务 |
---|---|---|
协调节点 | mongos 路由节点协调 | 主节点直接协调 |
性能损耗 | 高(跨节点通信开销) | 低 |
数据分布 | 可能涉及多个分片 | 单分片内部 |
最大操作时间 | 默认 60 秒(可配置) | 默认 60 秒 |
适用场景 | 跨分片数据一致性操作 | 单分片复杂操作 |
七、典型应用场景
场景 1:电商订单系统
javascript
// 订单创建 + 库存扣减(跨分片)
session.startTransaction();
orders.insertOne({...}); // 分片A
inventory.updateOne({...}); // 分片B
payments.insertOne({...}); // 分片C
session.commitTransaction();
场景 2:银行转账
javascript
// 跨账户转账(同一用户可能跨分片)
session.startTransaction();
accounts.update({_id: "A"}, {$inc: {balance: -100}}); // 分片X
accounts.update({_id: "B"}, {$inc: {balance: 100}}); // 分片Y
session.commitTransaction();
通过合理设计,分片集群事务可支持每秒数千次操作(具体性能取决于分片数量和负载)。建议使用 MongoDB Atlas 的全球集群功能自动优化跨区域事务延迟。关键生产系统应配合应用层重试机制和熔断策略实现最终可靠性。
八、安全机制
-
认证方式
- SCRAM(默认)
- x.509 证书认证
- LDAP/Kerberos 集成
-
权限控制
- 角色管理(内置角色与自定义角色)
javascriptdb.createRole({ role: "appReadWrite", privileges: [{ resource: { db: "appDB", collection: "" }, actions: ["find", "insert", "update"] }], roles: [] })
-
网络加密
- TLS/SSL 配置
yamlnet: ssl: mode: requireSSL PEMKeyFile: /etc/ssl/mongo.pem
九、备份与恢复
-
逻辑备份工具
mongodump
/mongorestore
bashmongodump --uri="mongodb://user:pwd@host:27017/mydb" --out=/backup
-
物理备份策略
- 文件系统快照
- Oplog 增量恢复
十、监控与调优
-
性能工具
mongostat
(实时状态监控)mongotop
(集合读写分析)- 数据库命令:
db.currentOp()
,db.serverStatus()
-
慢查询分析
javascriptdb.setProfilingLevel(1, { slowms: 100 }) // 开启慢查询日志
十一、扩展与集成
-
Change Streams
javascriptconst changeStream = db.orders.watch([{ $match: { operationType: "insert" } }]); changeStream.on("change", (change) => { console.log("New order:", change.fullDocument); });
-
与大数据集成
- MongoDB Connector for Spark
- 导出到 Hadoop(HDFS)
十二、云服务与工具
-
MongoDB Atlas
- 自动分片配置
- 跨区域容灾部署
-
GUI 工具
- MongoDB Compass(可视化查询)
- Robo 3T(轻量级客户端)