Article
第一章:MongoDB 基础概念
1.1 什么是 MongoDB
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| MongoDB | 一个开源的 NoSQL 文档数据库,以 BSON 格式存储数据,支持灵活的模式设计 | 不是关系型数据库,不支持 SQL(原生),但提供类似 SQL 的查询语法 |
| NoSQL | ”Not Only SQL”,泛指非关系型数据库,强调可扩展性、高性能与灵活数据模型 | 并非完全取代 SQL,而是适用于特定场景(如高并发写入、非结构化数据) |
| 文档数据库 | 以”文档”为基本单位存储数据,通常使用类 JSON 结构 | 文档之间可具有不同字段,无需预定义表结构 |
| MongoDB 适用领域 | 内容管理系统、实时分析、物联网、日志存储、用户配置管理等 | 不适合强事务、复杂多表关联或 ACID 要求极高的金融核心系统 |
1.2 文档、集合与数据库
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| 数据库(Database) | 顶层容器,用于组织集合;每个 MongoDB 实例可包含多个数据库 | 数据库名区分大小写,不能包含空字符或保留字符(如 / \ . " $) |
| 集合(Collection) | 类似于关系型数据库中的”表”,是文档的容器;无固定 schema | 集合名不能以 system. 开头(保留用途),且不能包含 $ 字符 |
| 文档(Document) | MongoDB 中的基本数据单元,采用 BSON 格式,结构为键值对嵌套对象 | 文档必须包含 _id 字段(唯一标识),若未提供则自动生成 ObjectId |
_id 字段 | 每个文档的唯一主键,默认为 ObjectId 类型 | 可手动指定 _id,但必须保证在集合内唯一;不可修改 |
| 命名空间(Namespace) | 格式为 database.collection,唯一标识一个集合 | 在内部操作和日志中常以命名空间形式出现 |
1.3 BSON 与 JSON 的区别
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| JSON | JavaScript Object Notation,一种轻量级数据交换格式,人类可读 | 仅支持字符串、数字、布尔、数组、对象、null 六种类型 |
| BSON | Binary JSON,MongoDB 使用的二进制编码序列化格式 | 是 JSON 的超集,支持更多数据类型,并优化了存储与遍历效率 |
| BSON 支持的数据类型 | 包括:ObjectId、Date、Binary Data、Timestamp、Decimal128、Int32/64 等 | 在 Shell 中输入 new Date() 或 ObjectId("...") 可创建对应类型 |
| 存储效率 | BSON 为二进制格式,比纯文本 JSON 更紧凑,解析速度更快 | 虽然不可读,但 mongosh 会自动将其显示为可读形式 |
| 类型保留 | JSON 无法区分整数与浮点数(统一为 number),BSON 可明确区分 Int32/64/Double | 在应用层需注意数值类型的精度问题,避免意外转换 |
1.4 MongoDB 的核心特性
| 特性名称 | 说明 | 注意事项 |
|---|---|---|
| 动态 Schema(灵活模式) | 同一集合中的文档可拥有不同字段结构,无需预先定义表结构 | 提高开发敏捷性,但需在应用层加强数据一致性校验 |
| 高性能读写 | 支持内存映射文件、索引优化、批量写入等机制 | 默认写操作不等待磁盘确认(fire-and-forget),可通过 writeConcern 控制可靠性 |
| 水平扩展(Sharding) | 通过分片将数据分布到多个服务器,支持 PB 级数据存储 | 需合理选择分片键,避免热点和数据倾斜 |
| 高可用(Replica Set) | 自动故障转移、数据冗余,主从复制架构 | 至少需要 3 个节点(含仲裁节点)才能实现自动选举 |
| 丰富的查询语言 | 支持条件查询、正则匹配、地理空间查询、聚合管道等 | 查询能力接近 SQL,但语法不同,需学习操作符(如 $match、$group) |
| 原子性操作 | 对单个文档的操作是原子的(包括嵌套字段更新) | 不支持跨文档事务(4.0+ 支持多文档 ACID 事务,但有性能开销) |
| GridFS | 用于存储大文件(>16MB),将文件分块存入两个集合(chunks + files) | 适用于音视频、日志等大对象存储,但元数据查询较复杂 |
第二章:MongoDB 安装与环境配置
2.1 在不同操作系统上安装 MongoDB
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| Windows 安装 | 1. 从官网下载 MSI 安装包 2. 运行安装向导,选择”Complete”模式 3. 勾选”Install MongoD as a Service”可自动注册服务 | 默认安装路径为 C:\Program Files\MongoDB;需手动创建 data\db 目录或指定 --dbpath |
| macOS 安装(Homebrew) | 执行命令:brew tap mongodb/brewbrew install mongodb-community | 安装后使用 brew services start mongodb-community 启动服务 |
| Ubuntu/Debian 安装 | 1. 导入公钥:wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -2. 添加源并安装: echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu $(lsb_release -cs)/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.listsudo apt update && sudo apt install -y mongodb-org | 需根据 Ubuntu 版本(如 jammy、focal)替换 $(lsb_release -cs);安装后默认未启动 |
| CentOS/RHEL 安装 | 类似 Ubuntu,使用 yum/dnf 添加 MongoDB 官方 repo 后安装 mongodb-org 包 | 需关闭 SELinux 或配置策略,否则可能无法写入数据目录 |
| 验证安装 | 执行 mongod --version 或 mongosh --version 查看版本号 | 若命令未找到,需将 MongoDB 的 bin 目录加入系统 PATH |
2.2 启动与连接 MongoDB Shell
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 启动 mongod 服务 | 命令:mongod --dbpath /path/to/data/db(若已配置服务,则用 sudo systemctl start mongod) | 必须确保 --dbpath 指向的目录存在且有读写权限;默认端口 27017 |
| 后台运行 mongod | 添加 --fork --logpath /var/log/mongodb.log 参数 | --fork 要求必须指定 --logpath,否则启动失败 |
| 连接 MongoDB Shell | 执行命令:mongosh 或指定主机端口:mongosh "mongodb://localhost:27017" | 若启用了认证,需加 --username 和 --password 参数 |
| 切换数据库 | 在 mongosh 中执行:use mydb | 即使数据库不存在,切换也会成功;只有插入数据后才真正创建 |
| 查看当前数据库 | 执行:db.getName() | 返回当前上下文数据库名 |
| 退出 Shell | 输入:exit 或 quit() | 不会停止 mongod 服务,仅断开客户端连接 |
2.3 配置文件与服务管理
| 配置项/操作 | 说明与语法示例 | 注意事项 |
|---|---|---|
| 配置文件路径 | 默认位置: - Linux: /etc/mongod.conf- Windows: 安装目录下的 bin\mongod.cfg | 使用 YAML 格式(注意缩进),注释以 # 开头 |
| 常用配置参数 | 示例:yaml<br/>storage:<br/> dbPath: /var/lib/mongodb<br/>net:<br/> port: 27017<br/> bindIp: 127.0.0.1,192.168.1.10<br/>security:<br/> authorization: enabled<br/> | 修改 bindIp 可允许远程连接,但需配合防火墙和认证使用 |
| 使用配置文件启动 | 命令:mongod -f /etc/mongod.conf | 若同时指定命令行参数和配置文件,命令行优先级更高 |
| 注册为系统服务 | Ubuntu/Debian:sudo systemctl enable mongodWindows:安装时勾选服务选项 | 服务方式启动会自动读取默认配置文件 |
| 重载配置 | MongoDB 不支持动态重载配置,需重启服务生效 | 可通过 db.adminCommand({getParameter: 1, ...}) 查询运行时参数 |
| 日志管理 | 配置中设置:yaml<br/>systemLog:<br/> destination: file<br/> path: /var/log/mongodb/mongod.log<br/> logAppend: true<br/> | 日志文件过大时可手动轮转,或使用 logrotate 工具 |
2.4 常用命令行工具简介(mongosh、mongodump、mongoexport 等)
| 工具名称 | 用途说明 | 基本语法示例 | 注意事项 |
|---|---|---|---|
| mongosh | 官方交互式 Shell,用于执行数据库操作 | mongosh "mongodb://user:pass@localhost:27017/mydb" | 替代旧版 mongo shell,功能更强大,支持语法高亮和自动补全 |
| mongodump | 备份数据库或集合为 BSON 文件 | mongodump --db mydb --collection users --out /backup/ | 默认输出到 dump/ 目录;支持 --uri、--gzip 等参数 |
| mongorestore | 从 mongodump 生成的备份恢复数据 | mongorestore --db newdb /backup/mydb/users.bson | 若目标集合已存在,默认会插入(不覆盖),可用 --drop 先删除 |
| mongoexport | 将集合导出为 JSON 或 CSV 格式(适合外部系统交换) | mongoexport --db mydb --collection users --type=csv --fields name,email --out users.csv | 仅支持导出单个集合;嵌套字段需用点号表示(如 address.city) |
| mongoimport | 从 JSON/CSV 文件导入数据到集合 | mongoimport --db mydb --collection users --type=csv --headerline --file users.csv | CSV 必须包含标题行(除非用 --fields 显式指定);JSON 需每行一个文档(非数组) |
| bsondump | 查看 BSON 文件内容(调试用) | bsondump dump/mydb/users.bson | 输出为可读格式,便于验证备份内容 |
| mongostat | 实时监控 MongoDB 性能指标(类似 top) | mongostat --host localhost:27017 | 需对 admin 数据库有权限;显示 insert/update/delete 等操作频率 |
| mongotop | 查看集合级别的读写耗时 | mongotop --host localhost:27017 | 按集合统计读写时间(毫秒),帮助定位热点集合 |
第三章:数据库与集合操作
3.1 创建与切换数据库
| 操作名称 | 语法 / 命令 | 用途说明 | 注意事项 |
|---|---|---|---|
| 切换/创建数据库 | use <database_name> | 在 mongosh 中切换当前上下文数据库;若数据库不存在,则在首次插入数据时自动创建 | 仅执行 use 不会立即创建数据库,必须插入至少一个文档才会持久化 |
| 查看当前数据库 | db.getName() | 返回当前 Shell 上下文所处的数据库名 | 与 db 对象等价,db 即代表当前数据库 |
| 列出所有数据库 | show dbs 或 db.adminCommand({listDatabases: 1}) | 显示所有非空数据库(或有权限访问的数据库) | 空数据库(无集合或无数据)默认不显示,需插入数据后才可见 |
| 删除当前数据库 | db.dropDatabase() | 删除当前上下文数据库及其所有集合和索引 | 需对目标数据库有写权限;操作不可逆 |
| 验证数据库是否存在 | 通过 show dbs 查看,或查询 admin.system.databases 集合(需管理员权限) | 用于脚本中判断数据库是否已创建 | 普通用户通常无法直接访问 system.databases |
3.2 创建、查看与删除集合
| 操作名称 | 语法 / 命令 | 用途说明 | 注意事项 |
|---|---|---|---|
| 显式创建集合 | db.createCollection("<collection_name>", { options }) | 手动创建集合,可指定选项(如 capped、size、validator 等) | 大多数情况下无需显式创建,插入文档时自动创建集合 |
| 隐式创建集合 | db.<collection_name>.insertOne({...}) | 首次向不存在的集合插入文档时自动创建 | 最常用方式,适合动态 schema 场景 |
| 查看当前数据库所有集合 | show collections 或 db.getCollectionNames() | 列出当前数据库中所有集合名称 | show collections 更简洁,getCollectionNames() 返回数组便于脚本处理 |
| 获取集合对象 | db.<collection_name> 或 db.getCollection("<collection_name>") | 返回集合的引用对象,用于后续操作 | 若集合名含特殊字符(如 -),必须使用 getCollection() |
| 删除集合 | db.<collection_name>.drop() | 删除整个集合及其索引、数据 | 返回 true 表示成功,false 表示集合不存在;操作不可逆 |
3.3 集合选项与 capped 集合
| 选项/概念名称 | 语法 / 说明 | 用途说明 | 注意事项 |
|---|---|---|---|
| capped 集合 | db.createCollection("logs", { capped: true, size: 100000, max: 5000 }) | 固定大小的循环集合,适用于日志、事件流等场景 | 一旦创建,不能转为普通集合;size(字节)必填,max(文档数)可选 |
| size(字节限制) | size: <number> | 指定 capped 集合的最大存储空间(单位:字节) | 实际分配可能略大于指定值;必须足够容纳至少一个文档 |
| max(文档数量上限) | max: <number> | 限制 capped 集合最多包含的文档数量 | 若同时设置 size 和 max,以先达到者为准 |
| validator(文档验证) | validator: { $jsonSchema: { ... } } 或 { field: { $type: "string" } } | 在插入/更新时校验文档结构 | 仅对新写入生效,不影响已有数据;验证失败会抛出错误 |
| validationLevel | validationLevel: "strict"(默认)或 "moderate" | 控制验证严格程度 | "moderate" 允许修改已有不符合规则的文档 |
| validationAction | validationAction: "error"(默认)或 "warn" | 验证失败时的行为 | "warn" 仅记录日志但允许写入(不推荐生产使用) |
| usePowerOf2Sizes(已弃用) | 旧版本用于优化存储分配 | MongoDB 3.0+ 已移除该选项 | 无需使用 |
3.4 命名规范与限制
| 命名对象 | 规范与限制 | 说明 | 注意事项 |
|---|---|---|---|
| 数据库名 | 不能为空 不能包含 / \ . " $ 空格等字符区分大小写 长度 ≤ 64 字节 | 例如:myApp_db 合法,my.db 或 my$db 非法 | Windows 文件系统对大小写不敏感,但 MongoDB 仍视为不同数据库 |
| 集合名 | 不能以 system. 开头(保留)不能包含 $(驱动可能允许,但不推荐)不能为空 区分大小写 | 例如:user_profiles 合法,$cmd 是内部命令集合,system.users 用于用户管理 | 虽然某些驱动支持 $,但 Shell 和工具链可能出错 |
| 字段名(文档内) | 不能包含 .(点号)和 $(美元符)作为字段名开头_id 为保留字段 | 例如:user.name 是嵌套字段合法,但字段名本身不能是 "user.name" | 可通过 $rename 修改非法字段名(若已存在),但应避免创建 |
| 特殊集合名 | oplog.rs、system.indexes、system.profile 等 | MongoDB 内部使用,用户不应手动操作 | 直接修改可能导致数据库异常 |
| 命名建议 | 使用小写字母、下划线分隔(snake_case),语义清晰 | 如:order_items、user_sessions | 避免使用复数形式争议(如 user vs users),团队统一即可 |
第四章:文档 CRUD 操作
4.1 插入文档(insertOne / insertMany)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| insertOne | db.collection.insertOne(document, options?) | 插入单个文档 | db.users.insertOne({ name: "Alice", age: 30 }) | 若未提供 _id,自动生成 ObjectId;若提供重复 _id,抛出错误 |
| insertMany | db.collection.insertMany([doc1, doc2, ...], options?) | 批量插入多个文档 | db.users.insertMany([{ name: "Bob" }, { name: "Carol" }]) | 默认按顺序插入,遇到错误则停止(ordered: true);可设 ordered: false 继续执行 |
| ordered 选项 | { ordered: true | false } | 控制批量插入是否有序 | db.users.insertMany([...], { ordered: false }) | false 可提高吞吐,但错误文档位置不确定;部分成功时返回 insertedIds 和 writeErrors |
| writeConcern | { writeConcern: { w: <value>, j: <bool>, wtimeout: <ms> } } | 控制写操作确认级别 | db.users.insertOne({...}, { writeConcern: { w: "majority" } }) | w: 1(默认)表示主节点确认;w: "majority" 表示多数副本确认;j: true 要求写入日志 |
4.2 查询文档(find / findOne)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| find | db.collection.find(query?, projection?) | 返回匹配文档的游标(可链式调用 sort/limit 等) | db.users.find({ age: { $gt: 25 } }, { name: 1, _id: 0 }) | 不加 .toArray() 在 Shell 中自动打印前 20 条;结果为游标,非数组 |
| findOne | db.collection.findOne(query?, projection?) | 返回第一个匹配文档(或 null) | db.users.findOne({ name: "Alice" }) | 直接返回对象,无需遍历;适合唯一查询(如通过 _id) |
| 查询条件 | { field: value } 或使用操作符 { field: { $op: value } } | 指定筛选条件 | db.users.find({ status: "active", age: { $gte: 18 } }) | 多条件默认为 $and;空查询 {} 返回全部文档 |
| 投影(Projection) | { field1: 1, field2: 0 } | 控制返回字段(1 包含,0 排除) | db.users.find({}, { name: 1, email: 1, _id: 0 }) | _id 默认包含,需显式设为 0 才排除;不能混用 0 和 1(除 _id 外) |
| 游标操作 | .sort(), .skip(), .limit(), .count() | 对查询结果进一步处理 | db.users.find().sort({ age: -1 }).limit(10) | 链式调用顺序影响性能,建议先 filter 再 sort/limit |
4.3 更新文档(updateOne / updateMany / replaceOne)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| updateOne | db.collection.updateOne(filter, update, options?) | 更新第一个匹配文档 | db.users.updateOne({ name: "Alice" }, { $set: { age: 31 } }) | 必须使用更新操作符(如 $set、$inc),否则报错 |
| updateMany | db.collection.updateMany(filter, update, options?) | 更新所有匹配文档 | db.users.updateMany({ status: "inactive" }, { $set: { archived: true } }) | 同上,必须使用更新操作符 |
| replaceOne | db.collection.replaceOne(filter, replacement, options?) | 完全替换第一个匹配文档(不保留原结构) | db.users.replaceOne({ _id: ObjectId("...") }, { name: "New", role: "admin" }) | 替换文档不能包含 _id 字段(除非与原值相同) |
| upsert 选项 | { upsert: true } | 若无匹配文档,则插入新文档 | db.users.updateOne({ email: "x@y.com" }, { $set: {...} }, { upsert: true }) | 插入时 _id 自动生成;常用于”存在则更新,否则创建”场景 |
| 返回结果 | 方法返回对象包含 matchedCount、modifiedCount、upsertedId 等 | 判断操作影响范围 | result.modifiedCount === 0 表示无变更 | modifiedCount 仅统计实际修改的文档(值未变则不计) |
4.4 删除文档(deleteOne / deleteMany)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| deleteOne | db.collection.deleteOne(filter, options?) | 删除第一个匹配文档 | db.users.deleteOne({ name: "Alice" }) | 即使有多个匹配,也只删一条;无匹配则不报错 |
| deleteMany | db.collection.deleteMany(filter, options?) | 删除所有匹配文档 | db.users.deleteMany({ status: "deleted" }) | 慎用空条件 {},会清空整个集合 |
| 返回结果 | 返回对象含 deletedCount | 获取删除文档数量 | if (result.deletedCount > 0) print("Deleted") | 无法恢复,操作不可逆 |
| 安全删除建议 | 先用 find 验证条件,再执行 delete | 避免误删 | db.users.find({ status: "test" });db.users.deleteMany({ status: "test" }) | 生产环境建议开启认证并限制删除权限 |
第五章:查询进阶
5.1 查询条件操作符($eq、$gt、$in、$regex 等)
| 操作符名称 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
$eq | { field: { $eq: value } } | 等于(通常可省略,直接写 { field: value }) | db.users.find({ age: { $eq: 30 } }) | $eq 主要用于与 $not 或聚合中明确表达相等 |
$ne | { field: { $ne: value } } | 不等于 | db.users.find({ status: { $ne: "inactive" } }) | 匹配字段不存在的文档(除非使用 $exists: true 限制) |
$gt / $gte | { field: { $gt: value } } | 大于 / 大于等于 | db.products.find({ price: { $gte: 100 } }) | 支持数字、日期、字符串(按字典序)比较 |
$lt / $lte | { field: { $lt: value } } | 小于 / 小于等于 | db.events.find({ date: { $lt: new Date("2025-01-01") } }) | 日期需用 new Date() 构造 |
$in | { field: { $in: [val1, val2, ...] } } | 字段值在指定数组中 | db.users.find({ role: { $in: ["admin", "moderator"] } }) | 数组元素类型需一致;支持 ObjectId、字符串等 |
$nin | { field: { $nin: [val1, val2, ...] } } | 字段值不在指定数组中 | db.users.find({ status: { $nin: ["banned", "deleted"] } }) | 同样匹配字段不存在的文档 |
$regex | { field: { $regex: /pattern/, $options: "i" } } | 正则匹配 | db.users.find({ name: { $regex: /^A/i } }) | 支持选项:i(忽略大小写)、m(多行)、x(扩展);性能较低,慎用于大集合 |
$exists | { field: { $exists: true | false } } | 判断字段是否存在 | db.users.find({ email: { $exists: true } }) | 常与 $ne: null 区分:null 字段存在但值为 null |
$type | { field: { $type: "string" } } | 按 BSON 类型筛选 | db.logs.find({ message: { $type: "string" } }) | 可用字符串(如 "int", "date")或数字代码(如 2 表示 string) |
$size | { arrayField: { $size: 3 } } | 匹配数组长度 | db.orders.find({ items: { $size: 2 } }) | 无法使用范围(如 $gt: 2),仅精确匹配 |
5.2 逻辑操作符($and、$or、$not、$nor)
| 操作符名称 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
$and | { $and: [ {expr1}, {expr2}, ... ] } | 所有条件同时成立(默认行为) | db.users.find({ $and: [ { age: { $gte: 18 } }, { status: "active" } ] }) | 通常可省略,直接写多个条件;仅当同一字段需多个条件时必须显式使用 |
$or | { $or: [ {expr1}, {expr2}, ... ] } | 任一条件成立 | db.users.find({ $or: [ { role: "admin" }, { vip: true } ] }) | 可与索引结合,但可能降低效率 |
$not | { field: { $not: { $gt: 100 } } } | 取反单个条件 | db.products.find({ price: { $not: { $gt: 500 } } }) | 不能直接用于整个表达式,需作用于具体操作符 |
$nor | { $nor: [ {expr1}, {expr2}, ... ] } | 所有条件都不成立 | db.users.find({ $nor: [ { banned: true }, { deleted: true } ] }) | 等价于 $not + $or;匹配字段缺失的文档 |
5.3 数组查询与嵌套文档查询
| 查询类型 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 数组元素匹配 | { tags: "mongodb" } | 数组包含指定值 | db.articles.find({ tags: "tutorial" }) | 自动遍历数组元素,无需 $elemMatch(单条件时) |
$elemMatch | { scores: { $elemMatch: { $gt: 80, $lt: 90 } } } | 数组中至少一个元素满足多个条件 | db.students.find({ exams: { $elemMatch: { subject: "math", score: { $gte: 90 } } } }) | 多条件作用于同一数组元素时必须使用 |
| 数组位置查询 | { "comments.0.author": "Alice" } | 查询数组特定位置的嵌套字段 | db.posts.find({ "comments.0.rating": { $gte: 4 } }) | 位置索引从 0 开始;若数组长度不足则不匹配 |
| 嵌套文档完全匹配 | { address: { street: "Main", city: "NYC" } } | 整个嵌套对象完全相等 | db.users.find({ contact: { email: "a@b.com", phone: "123" } }) | 字段顺序和数量必须完全一致 |
| 嵌套字段查询 | { "address.city": "NYC" } | 查询嵌套文档中的字段 | db.users.find({ "profile.age": { $gte: 25 } }) | 使用点号表示路径;支持任意深度 |
5.4 投影(Projection)与字段筛选
| 投影方式 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 包含字段 | { field1: 1, field2: 1 } | 仅返回指定字段 | db.users.find({}, { name: 1, email: 1 }) | _id 默认包含,需显式设 _id: 0 排除 |
| 排除字段 | { field1: 0, field2: 0 } | 返回除指定字段外的所有字段 | db.users.find({}, { password: 0, ssn: 0 }) | 不能与包含字段混用(除 _id 外) |
数组投影($slice) | { comments: { $slice: 5 } } | 限制返回数组的前 N 个元素 | db.posts.find({}, { comments: { $slice: -3 } }) | $slice: [skip, limit] 支持跳过+限制 |
条件投影($elemMatch) | { scores: { $elemMatch: { subject: "math" } } } | 仅返回数组中匹配的第一个元素 | db.students.find({}, { scores: { $elemMatch: { grade: { $gte: 90 } } } }) | 用于简化数组内容,避免返回整个数组 |
| 投影表达式(聚合风格) | 仅在聚合管道 $project 中支持 | 高级字段计算与重命名 | 见第七章聚合操作 | 普通 find 不支持表达式(如 $toUpper) |
5.5 排序、跳过与限制(sort / skip / limit)
| 方法名称 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| sort | .sort({ field: 1 | -1 }) | 按字段升序(1)或降序(-1)排序 | db.users.find().sort({ age: -1, name: 1 }) | 多字段排序按顺序生效;无索引时大数据集性能差 |
| skip | .skip(n) | 跳过前 n 条结果(用于分页) | db.products.find().skip(20).limit(10) | skip 值越大性能越差,建议配合唯一排序字段优化 |
| limit | .limit(n) | 限制返回最多 n 条文档 | db.logs.find().sort({ ts: -1 }).limit(100) | 常用于”最新 N 条”场景 |
| 组合使用 | .sort(...).skip(...).limit(...) | 实现分页查询 | db.orders.find({ userId: "..." }).sort({ createdAt: -1 }).skip(0).limit(10) | 建议始终先 sort 再 skip/limit,确保结果稳定 |
| 内存限制 | — | sort/skip/limit 在内存中处理 | 若结果集 > 32MB,操作失败 | 大数据分页建议使用游标或基于 _id 的范围查询 |
第六章:索引与性能优化
6.1 创建与管理索引(createIndex / getIndexes / dropIndex)
| 方法名称 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| createIndex | db.collection.createIndex(keys, options?) | 在集合上创建单字段或多字段索引 | db.users.createIndex({ email: 1 }) | 默认升序(1),降序用 -1;重复创建相同索引无副作用 |
| getIndexes | db.collection.getIndexes() | 获取集合所有索引信息 | db.products.getindexes() | 返回数组,包含 _id_ 索引及用户定义索引 |
| dropIndex | db.collection.dropIndex(indexName | keys) | 删除指定索引 | db.users.dropIndex("email_1") 或 db.users.dropIndex({ email: 1 }) | 不能删除 _id_ 索引;删除后查询性能可能下降 |
| dropIndexes | db.collection.dropIndexes() | 删除集合除 _id 外的所有索引 | db.logs.dropIndexes() | 慎用,通常用于重建索引前清理 |
| listIndexes | db.collection.listIndexes() | 返回索引游标(可用于聚合管道) | db.users.listIndexes().toArray() | 与 getIndexes() 功能类似,但返回游标 |
| background 选项 | { background: true } | 后台创建索引,不阻塞读写 | db.orders.createIndex({ status: 1 }, { background: true }) | 推荐在生产环境使用,避免服务中断;创建速度较慢 |
6.2 唯一索引、复合索引与多键索引
| 索引类型 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 唯一索引 | db.collection.createIndex({ field: 1 }, { unique: true }) | 确保字段值唯一(包括 null) | db.users.createIndex({ username: 1 }, { unique: true }) | 若已有重复值,创建失败;多个 null 被视为重复(除非使用 sparse) |
| sparse 选项 | { sparse: true } | 仅对存在该字段的文档建索引 | db.users.createIndex({ email: 1 }, { sparse: true, unique: true }) | 允许多个文档缺失该字段(即多个”null”不冲突) |
| 复合索引 | db.collection.createIndex({ field1: 1, field2: -1 }) | 支持多字段组合查询 | db.orders.createIndex({ userId: 1, createdAt: -1 }) | 字段顺序影响查询效率;最左前缀原则适用 |
| 多键索引(Multikey) | 对包含数组的字段自动创建 | 支持数组元素查询 | db.posts.createIndex({ tags: 1 }) | 自动识别;一个索引最多一个数组字段;不能作为分片键 |
| 文本索引 | db.collection.createIndex({ content: "text" }) | 支持全文搜索 | db.articles.createIndex({ title: "text", body: "text" }) | 一个集合只能有一个文本索引(可跨多字段);不支持排序 |
| 地理空间索引 | db.collection.createIndex({ location: "2dsphere" }) | 支持地理查询(如附近地点) | db.places.createIndex({ geo: "2dsphere" }) | 需字段为 GeoJSON 或 [lon, lat] 格式 |
6.3 索引使用分析(explain)
| 方法/选项 | 语法 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| explain() | db.collection.find(...).explain("executionStats") | 分析查询执行计划与性能 | db.users.find({ email: "a@b.com" }).explain("executionStats") | 模式:"queryPlanner"(默认)、"executionStats"、"allPlansExecution" |
| winningPlan | 返回结果中的 queryPlanner.winningPlan | 显示实际选择的执行计划 | 查看是否使用了索引(IXSCAN)或全表扫描(COLLSCAN) | COLLSCAN 表示未命中索引,需优化 |
| totalDocsExamined | executionStats.totalDocsExamined | 扫描文档总数 | 值远大于返回文档数(nReturned)表示效率低 | 理想情况:totalDocsExamined ≈ nReturned |
| totalKeysExamined | executionStats.totalKeysExamined | 扫描索引条目数 | 若 >0 且 nReturned 小,说明索引有效 | 0 表示未使用索引 |
| stage | winningPlan.stage | 执行阶段类型(如 COLLSCAN, IXSCAN, FETCH) | IXSCAN 表示索引扫描,FETCH 表示回表 | 多阶段组合反映完整流程 |
| indexBounds | winningPlan.inputStage.indexBounds | 显示索引扫描范围 | { "email": [ "[\"a@b.com\", \"a@b.com\"]" ] } | 范围越精确,效率越高 |
6.4 覆盖查询与性能调优建议
| 优化策略 | 说明 | 示例场景 | 注意事项 |
|---|---|---|---|
| 覆盖查询(Covered Query) | 查询字段和投影字段均在索引中,无需回表读取文档 | 索引:{ status: 1, updatedAt: -1 }查询: find({ status: "active" }, { _id: 0, updatedAt: 1 }) | 必须排除 _id(因不在索引中);显著提升性能 |
| 最左前缀原则 | 复合索引仅支持从左开始的连续字段查询 | 索引 (a, b, c) 支持 (a), (a,b), (a,b,c),但不支持 (b) 或 (a,c) | 设计索引时将高选择性字段放左侧 |
| 避免索引过多 | 每个索引占用存储并降低写入性能 | 写密集型集合(如日志)应限制索引数量 | 一般建议 ≤5 个索引/集合 |
| 使用 hint() 强制索引 | find(...).hint({ indexField: 1 }) | 测试不同索引效果或绕过查询优化器误判 | 生产环境慎用,可能随数据分布变化失效 |
| 监控慢查询 | 开启 profiler:db.setProfilingLevel(1, { slowms: 100 }) | 记录执行时间 >100ms 的查询到 system.profile | 定期分析慢查询日志,针对性优化 |
| 避免正则前导通配符 | { name: { $regex: "^Ali" } } 可用索引,{ name: { $regex: "li$" } } 不可用 | 优先使用前缀匹配 | 后缀或中缀正则无法使用索引,考虑文本索引或应用层处理 |
| 合理使用 sort + limit | 先筛选再排序,避免大结果集排序 | find({ active: true }).sort({ ts: -1 }).limit(10) | 若无索引支持 sort,内存可能溢出(>32MB 报错) |
第七章:聚合操作(Aggregation Pipeline)
7.1 聚合管道基本结构
| 概念名称 | 说明 | 语法示例 | 注意事项 |
|---|---|---|---|
| 聚合管道(Pipeline) | 由多个”阶段(stage)“组成的处理流程,每个阶段对文档流进行转换或筛选 | db.collection.aggregate([ { $match: {...} }, { $group: {...} } ]) | 阶段按顺序执行,前一阶段输出作为后一阶段输入 |
| 阶段(Stage) | 管道中的单个操作单元,以 $ 开头 | { $sort: { age: -1 } } | 每个阶段可出现多次(如多次 $match) |
| 输入文档流 | 聚合从集合中所有文档(或通过 $collStats 等特殊输入)开始 | 默认为集合全部文档 | 可通过 $match 尽早过滤以提升性能 |
| 输出结果 | 返回游标(Shell 中自动展开),或通过 $out / $merge 写入集合 | db.orders.aggregate([...]).toArray() | 结果不修改原集合(除非使用 $out) |
| 错误处理 | 任一阶段出错,整个聚合失败 | 如字段类型不匹配、内存超限等 | 使用 allowDiskUse: true 可缓解内存限制 |
7.2 常用阶段操作($match、$group、$project、$sort、$lookup 等)
| 阶段名称 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
$match | { $match: { <query> } } | 过滤文档(类似 find) | { $match: { status: "completed" } } | 应尽早使用以减少后续处理数据量 |
$group | { $group: { _id: <expr>, field: { <accumulator> } } } | 按字段分组并聚合计算 | { $group: { _id: "$category", total: { $sum: "$price" } } } | _id 为分组键,可为 null(全局聚合) |
$project | { $project: { <field>: <1 | 0 | expr> } } | 控制输出字段(包含/排除/重命名/计算) | { $project: { name: 1, fullName: { $concat: ["$first", " ", "$last"] } } } | 支持表达式;不能混用 0 和 1(除 _id) |
$sort | { $sort: { field: 1 | -1 } } | 对文档排序 | { $sort: { totalSales: -1 } } | 大结果集需索引支持,否则内存可能溢出 |
$lookup | { $lookup: { from: "otherColl", localField: "fk", foreignField: "_id", as: "joined" } } | 左外连接其他集合 | { $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "user" } } | as 字段为数组;性能较低,慎用于大集合 |
$unwind | { $unwind: "$arrayField" } 或 { $unwind: { path: "$tags", preserveNullAndEmptyArrays: true } } | 展开数组,每元素生成一行文档 | { $unwind: "$items" } | 默认跳过 null/空数组;可用选项保留 |
$limit | { $limit: <number> } | 限制输出文档数量 | { $limit: 10 } | 常用于分页或取 Top N |
$skip | { $skip: <number> } | 跳过指定数量文档 | { $skip: 20 } | 通常配合 $limit 实现分页 |
$addFields | { $addFields: { newField: <expr> } } | 添加新字段(不删除原字段) | { $addFields: { isAdult: { $gte: ["$age", 18] } } } | 等价于 $project 包含所有原字段 + 新字段 |
7.3 表达式与累加器($sum、$avg、$push 等)
| 类型 | 名称 | 语法示例 | 用途说明 | 代码示例(在 $group 中) | 注意事项 |
|---|---|---|---|---|---|
| 累加器 | $sum | { $sum: <expression> } | 求和 | totalQty: { $sum: "$quantity" } | 忽略非数字值;$sum: 1 可计数 |
| 累加器 | $avg | { $avg: <expression> } | 平均值 | avgPrice: { $avg: "$price" } | 自动忽略 null/非数字 |
| 累加器 | $push | { $push: <expression> } | 收集字段值到数组 | emails: { $push: "$email" } | 保留重复值;若字段缺失则 push null |
| 累加器 | $addToSet | { $addToSet: <expression> } | 收集唯一值到数组 | tags: { $addToSet: "$tag" } | 自动去重 |
| 累加器 | $first / $last | { $first: <field> } | 取分组中第一个/最后一个值 | firstLogin: { $first: "$loginTime" } | 依赖 $sort 顺序;无排序时不确定 |
| 表达式 | $cond | { $cond: { if: <bool>, then: <val>, else: <val> } } | 条件判断 | statusText: { $cond: { if: { $eq: ["$active", true] }, then: "Active", else: "Inactive" } } | 可嵌套 |
| 表达式 | $dateToString | { $dateToString: { format: "%Y-%m-%d", date: "$ts" } } | 日期格式化 | day: { $dateToString: { format: "%Y-%m-%d", date: "$createdAt" } } | 需指定时区(timezone)避免偏差 |
| 表达式 | $substr | { $substr: [ <string>, <start>, <length> ] } | 截取字符串 | initials: { $substr: ["$name", 0, 1] } | 起始位置从 0 开始 |
7.4 聚合性能与内存限制
| 优化项/限制 | 说明 | 应对措施 | 注意事项 |
|---|---|---|---|
| 内存限制(100MB) | 单个聚合阶段(如 $sort、$group)内存使用超过 100MB 会报错 | 添加 { allowDiskUse: true } 选项允许使用临时磁盘文件 | 仅 mongod 支持;mongosh 需显式传递选项:db.coll.aggregate([...], { allowDiskUse: true }) |
| 管道优化 | MongoDB 自动优化管道(如合并 $match、提前过滤) | 无需手动干预,但应将 $match 和 $project 放在前面 | 可通过 explain() 查看优化后的 pipeline |
| 索引利用 | $match 和 $sort 阶段可使用索引 | 为常用查询字段创建复合索引 | $lookup、$unwind 无法使用索引 |
避免大型 $group | 对全集分组且无筛选会导致高内存消耗 | 先 $match 缩小范围,或使用 $limit | 分页聚合建议基于 _id 范围查询而非 skip |
$lookup 性能 | 每个文档都会触发一次子查询,N+1 问题 | 被连接集合需有索引(foreignField);考虑应用层 join 或冗余设计 | 不适用于高频实时查询 |
| 结果大小限制 | 单个文档 ≤16MB;整个结果集受连接超时和内存限制 | 使用 $limit 控制输出;大数据导出用 $out 写入集合 | $out 会替换目标集合,$merge 可增量写入(4.2+) |
| 监控聚合性能 | 使用 explain("executionStats") 分析聚合计划 | 关注 usedDisk、spills(磁盘溢出次数) | 频繁 spill 表示需优化或增加内存 |
第八章:数据导入导出与备份恢复
8.1 使用 mongoimport / mongoexport
| 工具/操作 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| mongoimport | mongoimport --db=<db> --collection=<coll> --file=<file> [options] | 从 JSON/CSV/TSV 文件导入数据到集合 | mongoimport --db sales --collection orders --type=csv --headerline --file orders.csv | CSV 必须有标题行(或用 --fields 指定);JSON 需每行一个文档(非数组) |
--type | --type=json | csv | tsv | 指定输入文件格式 | --type=csv | 默认为 JSON;TSV 用制表符分隔 |
--headerline | --headerline | CSV 第一行作为字段名 | mongoimport ... --headerline | 若无标题行,必须用 --fields "name,age,email" 显式指定 |
--jsonArray | --jsonArray | 导入包含整个 JSON 数组的文件 | mongoimport --file data.json --jsonArray | 文件内容如 [{"a":1}, {"b":2}];性能低于逐行 JSON |
| mongoexport | mongoexport --db=<db> --collection=<coll> [options] --out=<file> | 将集合导出为 JSON 或 CSV | mongoexport --db users --collection profiles --type=csv --fields name,email --out profiles.csv | 仅支持单集合导出;嵌套字段用点号表示(如 address.city) |
--query | --query='{ "status": "active" }' | 导出满足条件的文档 | mongoexport --collection logs --query='{ "level": "error" }' --out errors.json | 查询语法同 find;需转义引号(Shell 中用单引号包裹) |
--limit / --skip | --limit=1000 --skip=500 | 控制导出数量与偏移 | 用于分批导出大集合 | 不保证顺序,建议配合 --sort |
8.2 使用 mongodump / mongorestore
| 工具/操作 | 语法示例 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| mongodump | mongodump --db=<db> --collection=<coll> --out=<dir> | 备份数据库或集合作为 BSON 文件 | mongodump --db inventory --out /backup/2025-02-01/ | 默认输出到 dump/ 目录;保留所有类型(包括 Date、ObjectId) |
--uri | --uri="mongodb://user:pass@host:port/db" | 指定连接字符串 | mongodump --uri="mongodb://localhost:27017/sales" | 支持认证、副本集、SSL 等高级连接 |
--gzip | --gzip | 压缩备份文件 | mongodump --db logs --gzip | 输出 .bson.gz 文件,节省空间 |
| mongorestore | mongorestore --db=<newdb> <dump_dir>/<db>/<coll>.bson | 从 mongodump 备份恢复数据 | mongorestore --nsFrom="old.*" --nsTo="new.*" dump/ | 默认插入(不覆盖);可用 --drop 先删除目标集合 |
--drop | --drop | 恢复前删除目标集合 | mongorestore --drop dump/inventory/products.bson | 防止数据重复;慎用于生产环境 |
--nsFrom / --nsTo | --nsFrom="prod.*" --nsTo="test.*" | 重命名数据库/集合(通配符) | 用于跨环境迁移(如 prod → test) | 支持 * 匹配;需 MongoDB 3.4+ |
| 增量备份 | 结合 oplog(需副本集) | 实现时间点恢复(PITR) | mongodump --oplog | 仅适用于副本集;单机模式不支持 |
8.3 CSV 与 JSON 格式处理
| 格式/操作 | 说明 | 处理要点 | 注意事项 |
|---|---|---|---|
| CSV 导入要求 | 每行一条记录;字段由逗号分隔;可选标题行 | 使用 --headerline 或 --fields 指定字段名 | 字段含逗号需用双引号包裹(如 "Smith, John") |
| CSV 导出限制 | 仅支持扁平结构;嵌套字段需用点号展开 | --fields "name,address.city" | 数组字段导出为字符串(如 ["a","b"] → "a,b"),无法还原 |
| JSON 行格式(每行一个文档) | { "name": "A" }{ "name": "B" } | mongoimport 默认格式;高效且支持大文件 | 不能是 JSON 数组(如 [ {...}, {...} ]),除非加 --jsonArray |
| JSON 数组格式 | [ { "name": "A" }, { "name": "B" } ] | 需使用 --jsonArray 选项 | 内存占用高,不适用于超大文件 |
| 特殊类型处理 | Date、ObjectId、Binary 等在 JSON 中需特殊表示 | 导出时自动转换为可读形式(如 ISODate("..."));导入时需保持格式一致 | 手动编辑 JSON 时勿修改类型标识,否则导入失败 |
| 编码问题 | 文件应为 UTF-8 编码 | 非 UTF-8(如 GBK)可能导致乱码 | 导入前用 iconv 转码:iconv -f gbk -t utf-8 input.csv > output.csv |
8.4 备份策略与注意事项
| 策略/项 | 说明 | 推荐做法 | 注意事项 |
|---|---|---|---|
| 全量备份频率 | 完整备份整个数据库 | 每日一次(结合增量);业务低峰期执行 | 大数据库备份耗时长,需监控磁盘空间 |
| 增量备份 | 仅备份自上次备份以来变更的数据 | 副本集环境下使用 mongodump --oplog;或基于应用日志实现 | 单机 MongoDB 无法原生支持增量备份 |
| 备份验证 | 定期恢复测试以确保备份有效 | 每月在隔离环境执行 restore + 查询验证 | ”能备份” ≠ “能恢复” |
| 存储位置 | 备份文件应异地存储(不同服务器/云存储) | 使用 rsync、aws s3 cp 等同步到远程 | 避免与数据库同机存放,防硬件故障 |
| 权限与安全 | 备份文件可能含敏感数据 | 加密存储(如 GPG);限制文件访问权限 | mongodump 不加密,需额外处理 |
| 自动化脚本 | 通过 cron(Linux)或 Task Scheduler(Windows)定时执行 | 示例:0 2 * * * /usr/bin/mongodump --gzip --out /backup/$(date +\%F) | 脚本需处理日志、错误通知、磁盘清理 |
| 锁与性能影响 | mongodump 默认不锁库,但大量读取可能影响性能 | 在从节点(Secondary)执行备份 | 主节点备份可能导致业务延迟 |
| Point-in-Time Recovery | 恢复到任意时间点 | 副本集 + oplog 备份;或结合文件系统快照 | 需开启 --replSet 并保留足够 oplog 窗口 |
第九章:用户权限与安全配置
9.1 创建用户与角色管理
| 操作名称 | 语法 / 命令 | 用途说明 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 创建用户 | db.createUser({ user: "<name>", pwd: "<password>", roles: [ ... ] }) | 在当前数据库创建新用户 | db.createUser({ user: "app_user", pwd: "secure123", roles: ["readWrite", "dbAdmin"] }) | 必须在目标数据库中执行(如 use myApp) |
| 内置角色 | "read", "readWrite", "dbAdmin", "userAdmin", "clusterAdmin" 等 | 预定义权限集合 | roles: ["readWrite", { role: "read", db: "analytics" }] | 角色作用域可跨库(通过 { role: "...", db: "..." }) |
| 自定义角色 | db.createRole({ role: "<name>", privileges: [...], roles: [...] }) | 定义细粒度权限 | db.createRole({ role: "orderReader", privileges: [{ resource: { db: "sales", collection: "orders" }, actions: ["find"] }], roles: [] }) | 需 userAdmin 或 userAdminAnyDatabase 权限 |
| 修改用户密码 | db.changeUserPassword("<username>", "<newPassword>") | 更新用户密码 | db.changeUserPassword("app_user", "newPass456") | 需对用户所在数据库有 changeOwnPassword 或更高权限 |
| 删除用户 | db.dropUser("<username>") | 删除指定用户 | db.dropUser("temp_user") | 无法删除当前会话用户;需先切换或退出 |
| 查看用户信息 | db.getUser("<username>") 或 db.getUsers() | 查询用户详情或列表 | db.getUsers({ showCredentials: false }) | 默认不显示密码哈希;需 viewUser 权限 |
9.2 数据库访问控制(Authentication & Authorization)
| 安全机制 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| 启用认证 | 强制客户端提供有效凭据才能访问数据库 | 启动 mongod 时加 --auth,或配置文件中设置 security.authorization: enabled | 未启用时所有连接均为 root 权限,极度危险 |
| SCRAM-SHA-1/256 | 默认认证机制(Salted Challenge Response Authentication Mechanism) | 创建用户时自动使用;无需额外配置 | SHA-256 更安全(MongoDB 4.0+),旧驱动可能仅支持 SHA-1 |
| 连接认证 | 客户端连接时指定用户名、密码、认证源 | mongosh "mongodb://app_user:pwd@localhost:27017/myApp?authSource=myApp" | authSource 指定用户所属数据库(通常为创建用户的库) |
| 角色继承 | 用户可拥有多个角色,权限合并 | roles: ["readWrite", "backup"] | 权限为”或”关系;无显式 deny 机制 |
| 最小权限原则 | 用户仅授予必要权限 | 应用用户不应拥有 dbAdmin 或 clusterAdmin | 避免使用 root 角色连接应用 |
| 认证失败行为 | 连续失败多次可能触发延迟(防暴力破解) | 由服务器自动处理 | 日志中记录 AuthenticationFailed 事件 |
9.3 网络绑定与防火墙设置
| 安全项 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| 绑定 IP 地址 | 限制 mongod 监听的网络接口 | 配置文件:net.bindIp: 127.0.0.1,192.168.1.10或启动参数 --bind_ip | 默认 127.0.0.1(仅本地);生产环境应绑定内网 IP,禁止 0.0.0.0 |
| 端口 | 默认 27017 | net.port: 27017 | 可修改,但需同步更新客户端连接串 |
| 防火墙规则 | 仅允许可信 IP 访问 MongoDB 端口 | Linux: ufw allow from 10.0.0.5 to any port 27017AWS: 安全组入站规则 | 禁止公网直接暴露 MongoDB 端口 |
| TLS/SSL 加密 | 加密客户端与服务器通信 | 配置 net.tls.mode: requireTLS + 证书路径 | 需有效证书;自签名证书需客户端信任 |
| 连接池限制 | 控制最大并发连接数 | net.maxIncomingConnections: 65536(默认) | 防止 DoS;根据系统资源调整 |
| 禁用 HTTP 接口 | 旧版本(<3.2)支持 REST/HTTP 接口,存在风险 | 确保未启用 --rest 或 httpinterface | 新版本已移除该功能 |
9.4 审计与日志配置
| 功能 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| 审计日志(Audit Log) | 记录敏感操作(如登录、删除、权限变更) | 企业版功能:auditLog: { destination: "file", format: "JSON", path: "/var/log/mongodb/audit.log" } | 社区版不支持;需 MongoDB Enterprise 或 Atlas |
| 系统日志级别 | 控制 mongod 输出日志详细程度 | systemLog.verbosity: 1(0~5,越高越详细)或运行时 db.setLogLevel(1) | 高 verbosity 影响性能;调试后应调低 |
| 慢查询日志 | 自动记录执行时间超过阈值的操作 | operationProfiling.slowOpThresholdMs: 100operationProfiling.mode: "slowOp" | 日志写入 system.profile 集合(需提前开启) |
| 日志轮转 | 防止日志文件无限增长 | systemLog.logRotate: "reopen" + 外部工具(logrotate) | logRotate: "rename" 为默认,需配合信号 SIGUSR1 |
| 敏感信息脱敏 | 日志中避免记录密码、密钥 | MongoDB 自动过滤命令中的凭据字段 | 但应用层日志仍需自行处理 |
| 日志存储位置 | 建议独立磁盘分区 | systemLog.path: "/var/log/mongodb/mongod.log" | 避免与数据目录共用,防止 I/O 争抢 |
第十章:副本集与分片集群(高可用架构)
10.1 副本集原理与部署
| 概念/操作 | 说明 | 配置/命令示例 | 注意事项 |
|---|---|---|---|
| 副本集(Replica Set) | 一组维护相同数据集的 mongod 实例,提供自动故障转移与数据冗余 | 至少 3 节点(1 主 + 1 从 + 1 仲裁 或 3 数据节点) | 节点数应为奇数,避免脑裂;偶数时需配置仲裁节点 |
| 主节点(Primary) | 接受所有写操作,将 oplog 复制到从节点 | 客户端仅向 Primary 写入 | 任一时刻仅一个 Primary |
| 从节点(Secondary) | 复制主节点 oplog 并异步应用,可配置为延迟复制或隐藏节点 | rs.add("host:port") 添加从节点 | 默认不可读(需设置 readPreference) |
| 仲裁节点(Arbiter) | 不存储数据,仅参与选举投票 | rs.addArb("arbiter-host:port") | 资源消耗低,但不提升读性能;不建议在主/从同机部署 |
| 初始化副本集 | 在主节点执行 rs.initiate() | js\nrs.initiate({\n _id: "rs0",\n members: [\n { _id: 0, host: "node1:27017" },\n { _id: 1, host: "node2:27017" }\n ]\n})\n | 所有节点必须能互相解析主机名并通信 |
| 查看状态 | rs.status() | 返回各节点健康状态、同步延迟、角色等 | 关注 optimeDate 判断是否落后 |
| 选举机制 | 当 Primary 失联(默认 10 秒),从节点发起选举 | 自动进行,无需干预 | 网络分区可能导致短暂无主;优先级(priority)影响胜出概率 |
| Oplog | 主节点的操作日志(capped collection),从节点据此同步 | 默认大小为 5% 磁盘(最大 50GB) | 若从节点落后超过 oplog 窗口,需全量同步(resync) |
10.2 分片集群架构与组件(shard, config server, mongos)
| 组件 | 作用说明 | 部署要求 | 注意事项 |
|---|---|---|---|
| Shard(分片) | 存储实际数据的副本集(或独立 mongod,不推荐) | 每个 shard 是一个完整副本集(至少 3 节点) | 数据按 chunk 分布在多个 shard 上 |
| Config Server | 存储集群元数据(如分片键范围、chunk 位置、数据库配置) | MongoDB 3.4+ 必须为副本集(3 节点),不再支持单机 | 元数据一致性关键;不可与 shard 共用节点 |
| mongos | 查询路由器,接收客户端请求,路由到正确 shard,合并结果 | 可部署多个(无状态),前端负载均衡 | 应用直连 mongos,而非 shard;需配置所有 config server 地址 |
| 启动 mongos | mongos --configdb <replSetName>/cfg1:27019,cfg2:27019,cfg3:27019 --port 27017 | 必须指定 config server 副本集名称和成员 | 不存储数据,崩溃后可重启 |
| 添加 Shard | 通过 mongos 执行 sh.addShard("rsName/host:port") | js\nsh.addShard("shard1/node1:27017,node2:27017")\n | shard 名称需全局唯一 |
| 启用数据库分片 | sh.enableSharding("<database>") | 必须先启用数据库,再对集合分片 | 仅该库下集合可分片 |
| 对集合分片 | sh.shardCollection("<db>.<coll>", { <shardKey>: 1 }) | js\nsh.shardCollection("sales.orders", { orderId: 1 })\n | 分片键一旦设定不可更改(除非重建集合) |
10.3 数据分布策略(哈希 vs 范围分片)
| 分片策略 | 说明 | 语法示例 | 优缺点与注意事项 |
|---|---|---|---|
| 范围分片(Range) | 按分片键值连续范围划分 chunk(如 A–M, N–Z) | sh.shardCollection("logs.events", { timestamp: 1 }) | ✅ 适合范围查询(如时间窗口) ❌ 易导致热点(新数据集中写入最后一个 chunk) |
| 哈希分片(Hash) | 对分片键计算哈希值,均匀分布 chunk | sh.shardCollection("users.profiles", { userId: "hashed" }) | ✅ 写入负载均衡 ❌ 范围查询需广播到所有 shard(性能差) |
| 复合分片键 | 多字段组合(如 { region: 1, userId: 1 }) | 支持范围+哈希混合(首字段范围,后续字段可优化分布) | 需根据查询模式设计;首字段选择至关重要 |
| Zone / Tag 感知分片 | 将特定数据范围(如 region="CN")固定到指定 shard | sh.addShardTag("shard1", "china")sh.addTagRange("db.coll", { region: "CN" }, { region: "CN" }, "china") | 用于数据本地化(GDPR)、冷热分离等场景 |
| Chunk 大小 | 默认 64MB | 可通过 db.adminCommand({ splitChunk: ..., size: 128 }) 调整(不推荐) | 过小 → 迁移频繁;过大 → 负载不均 |
| Balancer | 后台进程,自动迁移 chunk 以均衡各 shard 数据量 | sh.setBalancerState(true) 启用 | 可在业务低峰期运行;迁移期间可能影响性能 |
10.4 故障转移与读写分离
| 机制 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| 自动故障转移 | 副本集 Primary 宕机后,从节点自动选举新 Primary | 无需配置,依赖副本集心跳与选举协议 | 切换通常在 10~30 秒内完成 |
| 读偏好(Read Preference) | 控制客户端从哪个节点读取数据 | 连接字符串参数:?readPreference=secondary或驱动中设置 | 选项: - primary(默认)- primaryPreferred- secondary- secondaryPreferred- nearest |
| 读扩展 | 将只读查询路由到 Secondary,减轻 Primary 负载 | 应用层显式设置 readPreference=secondary | 最终一致性:Secondary 可能有毫秒级延迟 |
| 写关注(Write Concern) | 控制写操作确认级别 | { writeConcern: { w: "majority", j: true, wtimeout: 5000 } } | w: "majority" 确保多数节点确认,防回滚;j: true 要求写入日志 |
| 分片集群高可用 | Config Server 副本集保障元数据可用 每个 Shard 为副本集保障数据可用 多 mongos 避免单点 | 部署时确保各组件均有冗余 | mongos 无状态,可弹性扩缩 |
| 手动干预选举 | 临时阻止某节点成为 Primary(如维护) | rs.freeze(0) 解除冻结rs.stepDown(60) 主动降级 Primary | rs.reconfig() 可调整优先级 |
| 监控与告警 | 关注指标:Replication lag、Unreachable nodes、Balancer status | 使用 MongoDB Cloud Manager、Ops Manager 或 Prometheus Exporter | 延迟 > oplog 窗口将导致从节点失效 |