MongoDB 部署方式、架构选型与提效工具手册
先把开发实例隔离出来
项目第一次接入 MongoDB 时,最容易埋下的事故不是查询写错,而是开发机保存了生产连接、无认证容器暴露到局域网,或者旧数据卷让初始化脚本看似执行却没有生效。先把目标收窄到一个可删除、只绑定回环地址的开发实例,再在这个实例上验证用户、文档约束、索引和清理路径。
MongoDB 存的是 BSON 文档,不是“随便塞 JSON”的轻量容器。collection、schema validation 和 index 决定数据入口,WiredTiger 与 journal 决定本机存储行为,oplog、Replica Set 和 Sharded Cluster 则把问题扩展到复制、一致性与容量。后面的每一步都会回到这些运行对象,而不是停在“容器启动成功”。
动手前需要 Docker Engine 或 Docker Desktop,以及可用的 docker compose。命令行验证使用 mongosh;未安装本机客户端时可以执行容器内的 mongosh。mongodump 与 mongorestore 属于单独发布的 MongoDB Database Tools,不是 shell 内置命令。Compass 只在需要 GUI 复核时安装,并为连接设置醒目的环境名称。
创建一个隔离实验目录,例如 your-project/,为 MongoDB 预留宿主端口 27018。真实地址、用户名、密码、Atlas SRV 连接串、TLS 私钥和内网域名都不能进入仓库、截图或命令历史。先确认 Docker 与端口状态:
docker version
docker compose version
docker ps --format "table {{.Names}}\t{{.Ports}}\t{{.Status}}"Windows:
netstat -ano | findstr ":27017"
netstat -ano | findstr ":27018"Linux / macOS:
lsof -i :27017
lsof -i :27018如果本机已经有 MongoDB 占用 27017,继续使用 27018:27017,不要为了省事把实验临时指向共享库或生产库。
镜像示例固定为 8.3.4,避免 latest 或浮动系列标签在重建环境时悄悄换版本。8.3 是 minor release 轨道;MongoDB Versioning 说明 major release 提供可预测的五年生命周期,而 minor release 需要更频繁且按相邻版本顺序升级。需要手工控制升级窗口、Atlas Live Migration 或 mongosync 时,优先评估 major release;选择 minor release 时,则把兼容测试和连续升级写入维护计划。minor release 不能跨级升级,每一步都包含 binary 与 Feature Compatibility Version(FCV)变更;失败时也不能只把镜像 tag 改回去,必须先确认目标版本允许的 FCV、备份恢复点和驱动兼容性。驱动、mongosh、Compass 和 Database Tools 独立发版,升级服务端前应按 Client Library Compatibility 检查应用驱动,而不能用服务端版本号推断兼容性。
本机安装要从 MongoDB Installation 进入对应操作系统教程,并核对该版本的支持矩阵。Community 与 Enterprise 的可用平台、CPU 架构和安装包并不完全相同,MongoDB 不支持 32 位 x86;宿主系统升级到矩阵之外也不再属于官方支持配置。团队镜像模板仍要单独验证实际 CPU 架构,而不是用“Docker 能拉取”替代平台支持判断。
Community Server 使用 Server Side Public License(SSPL);Enterprise Advanced 使用商业许可,Atlas 还受对应服务条款约束。普通应用把 Community Server 当数据库使用,与把修改后的发行版交付、嵌入客户现场、或向第三方提供 MongoDB 托管服务不是同一种许可场景。后几类交付必须按 MongoDB Licensing 复核 SSPL 义务并留存法务结论,不能把 Community 的免费获取等同于任意商业分发。
下面会同时出现两种镜像:mongodb/mongodb-community-server 由 MongoDB 维护,mongo 是 Docker Official Image 并提供初始化环境变量与 /docker-entrypoint-initdb.d/ 约定。团队必须选定一种作为模板主源,记录精确 tag、初始化语义和升级验证,不能把两者当成完全相同的入口。
| 入口 | 适合 | 不适合 | 必须确认 |
|---|---|---|---|
| 本机安装 | 学习 mongod、mongosh、Database Tools | 团队统一模板 | 版本、数据目录、服务自启动、卸载 |
| Docker 单容器 | 个人最小验证、快速复现连接和索引 | 团队长期共享、复制集能力验证 | root 密码、端口、volume、清理 |
| Docker Compose | 项目本地依赖、脚本化启动、初始化用户和 collection | 生产部署 | .env、init scripts、healthcheck、只绑定本机 |
| 单节点 Replica Set | 本地验证 transactions、change streams、retryable writes 的基础行为 | 生产高可用 | replSetName、keyfile、初始化顺序 |
| 自建 Replica Set | 生产基础高可用、读扩展、故障切换 | 没有 DBA / SRE 能力的团队 | 三节点、投票、oplog、备份、监控 |
| Sharded Cluster | 海量数据、写入和容量水平扩展 | 中小业务过早复杂化 | shard key、chunk、balancer、跨分片查询 |
| Atlas / 托管 MongoDB | 降低运维复杂度、快速生产化 | 数据边界、成本、网络和合规未确认场景 | VPC、IP allowlist、备份、审计、退出 |
| Kubernetes Operator | 平台化、集群内自助交付 | 单个项目临时依赖 | CRD、secret、storage、升级和恢复 |
Docker 单容器
如果只想按 MongoDB 官方维护的 Community Server 镜像做无认证个人烟测,可以先跑:
docker run -d \
--name te-mongodb-community \
-p 127.0.0.1:27018:27017 \
mongodb/mongodb-community-server:8.3.4-ubi9-slim验证:
mongosh "mongodb://127.0.0.1:27018/?directConnection=true" \
--eval "db.adminCommand({ ping: 1 })"这个入口只用于个人隔离烟测,不进入共享环境模板。共享环境必须启用认证、最小权限和网络隔离。
清理:
docker rm -f te-mongodb-community如果要验证 Docker Official Image 的初始化变量和 root 用户,再使用下面的入口。
为隔离容器创建独立网络:
docker network create te-mongodb-dev启动:
docker run -d \
--name te-mongodb-single \
--network te-mongodb-dev \
-p 127.0.0.1:27018:27017 \
-e MONGO_INITDB_ROOT_USERNAME=root \
-e MONGO_INITDB_ROOT_PASSWORD=YOUR_STRONG_ROOT_PASSWORD \
mongo:8.3.4验证:
export MONGODB_ROOT_PASSWORD="YOUR_STRONG_ROOT_PASSWORD"
mongosh "mongodb://root:${MONGODB_ROOT_PASSWORD}@127.0.0.1:27018/admin?directConnection=true" \
--eval "db.adminCommand({ ping: 1 })"清理:
docker rm -f te-mongodb-single
docker network rm te-mongodb-dev注意:单容器适合验证认证、连接、collection 和 index,不代表复制集、事务、故障切换和备份恢复已经准备好。
Docker Compose
项目模板建议放到明确目录:
dev-dependencies/
mongodb/
compose.yaml
.env.example
init/01-create-users.js
init/02-create-collections.js
scripts/verify.sh
scripts/clean-dry-run.sh
scripts/clean-confirmed.sh.env.example:
MONGODB_VERSION=8.3.4
MONGODB_ROOT_USERNAME=root
MONGODB_ROOT_PASSWORD=YOUR_STRONG_ROOT_PASSWORD
MONGODB_APP_DATABASE=te_app
MONGODB_APP_USERNAME=te_app
MONGODB_APP_PASSWORD=YOUR_APP_PASSWORD
MONGODB_READONLY_USERNAME=te_readonly
MONGODB_READONLY_PASSWORD=YOUR_READONLY_PASSWORD
MONGODB_PORT=27018compose.yaml:
services:
mongodb:
image: mongo:${MONGODB_VERSION}
container_name: your-project-mongodb
command: ["mongod", "--auth", "--bind_ip_all"]
environment:
MONGO_INITDB_ROOT_USERNAME: ${MONGODB_ROOT_USERNAME}
MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_ROOT_PASSWORD}
MONGO_INITDB_DATABASE: ${MONGODB_APP_DATABASE}
MONGODB_APP_DATABASE: ${MONGODB_APP_DATABASE}
MONGODB_APP_USERNAME: ${MONGODB_APP_USERNAME}
MONGODB_APP_PASSWORD: ${MONGODB_APP_PASSWORD}
MONGODB_READONLY_USERNAME: ${MONGODB_READONLY_USERNAME}
MONGODB_READONLY_PASSWORD: ${MONGODB_READONLY_PASSWORD}
ports:
- "127.0.0.1:${MONGODB_PORT:-27018}:27017"
volumes:
- mongodb-data:/data/db
- ./init:/docker-entrypoint-initdb.d:ro
healthcheck:
test:
[
"CMD-SHELL",
"mongosh --quiet --username $$MONGO_INITDB_ROOT_USERNAME --password $$MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin --eval 'db.adminCommand({ ping: 1 })' >/dev/null || exit 1"
]
interval: 10s
timeout: 5s
retries: 30
volumes:
mongodb-data:这里使用 mongo 是因为它提供本地开发常用的初始化变量和脚本机制。若团队要求统一使用 MongoDB 上游维护的 mongodb/mongodb-community-server,则不要照搬这些环境变量,改为显式 mongosh 初始化脚本,并把差异写进项目 README。
启动:
docker compose --env-file dev-dependencies/mongodb/.env \
-f dev-dependencies/mongodb/compose.yaml \
up -d查看日志:
docker logs your-project-mongodb --tail 100停止但保留数据:
docker compose --env-file dev-dependencies/mongodb/.env \
-f dev-dependencies/mongodb/compose.yaml \
down个人环境重置数据:
docker compose --env-file dev-dependencies/mongodb/.env \
-f dev-dependencies/mongodb/compose.yaml \
down -v共享环境不能随手 down -v。要先确认 owner、数据库名、collection prefix、备份和清理窗口。
单节点 Replica Set
很多驱动能力、事务、change streams 和 retryable writes 的验证需要 Replica Set 语义。个人开发可以用单节点 Replica Set,但要明确它不是高可用:
services:
mongodb-rs:
image: mongo:${MONGODB_VERSION}
command:
- mongod
- --auth
- --replSet
- rs0
- --bind_ip_all
- --keyFile
- /etc/mongo-keyfile/keyfile
volumes:
- mongodb-rs-data:/data/db
- ./keyfile:/etc/mongo-keyfile/keyfile:ro初始化后需要执行:
rs.initiate({
_id: "rs0",
members: [{ _id: 0, host: "mongodb-rs:27017" }]
})keyfile 是成员内部认证,不是普通应用密码。文件权限、挂载路径、轮换和泄露处理都要单独治理。Windows / macOS Docker Desktop 上的 bind mount 权限可能导致 keyfile 不可用,团队模板要把这个风险写进 README。
共享开发实例
共享实例不是“大家连同一个 root”。它至少要有:
每个项目独立 database,例如 te_app_dev。每个项目独立 app、readonly、ops 账号。collection 命名带 owner 或业务前缀。
Compass 连接模板默认只读。删除脚本 dry-run 后再确认。root 账号只给平台 owner,不给普通开发长期使用。
共享实例适合重型数据、联调和多人协作,不适合随意 drop database、压测写爆磁盘、验证分片或高危权限。
环境变量
| 配置 | 示例 | 说明 |
|---|---|---|
MONGODB_VERSION | 8.3.4 | 固定到已验证的具体 patch tag |
MONGODB_ROOT_USERNAME | root | 只用于初始化和救援 |
MONGODB_ROOT_PASSWORD | YOUR_STRONG_ROOT_PASSWORD | 不提交真实值 |
MONGODB_APP_DATABASE | te_app | 项目数据库 |
MONGODB_APP_USERNAME | te_app | 应用账号 |
MONGODB_APP_PASSWORD | YOUR_APP_PASSWORD | 应用密码 |
MONGODB_READONLY_USERNAME | te_readonly | 只读排障账号 |
MONGODB_PORT | 27018 | 宿主端口 |
初始化用户
init/01-create-users.js:
const databaseName = process.env.MONGODB_APP_DATABASE || "te_app";
const appUser = process.env.MONGODB_APP_USERNAME || "te_app";
const appPassword = process.env.MONGODB_APP_PASSWORD || "YOUR_APP_PASSWORD";
const readonlyUser = process.env.MONGODB_READONLY_USERNAME || "te_readonly";
const readonlyPassword = process.env.MONGODB_READONLY_PASSWORD || "YOUR_READONLY_PASSWORD";
const appDb = db.getSiblingDB(databaseName);
appDb.createUser({
user: appUser,
pwd: appPassword,
roles: [{ role: "readWrite", db: databaseName }]
});
appDb.createUser({
user: readonlyUser,
pwd: readonlyPassword,
roles: [{ role: "read", db: databaseName }]
});重点:root 用户在 admin 库,业务用户建议放在业务库。连接串里 authSource 必须和用户所在库一致。
初始化 collection 与索引
init/02-create-collections.js:
const databaseName = process.env.MONGODB_APP_DATABASE || "te_app";
const appDb = db.getSiblingDB(databaseName);
appDb.createCollection("article_demo", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["slug", "title", "status", "owner", "createdAt"],
properties: {
slug: { bsonType: "string" },
title: { bsonType: "string" },
status: { enum: ["draft", "published"] },
owner: { bsonType: "string" },
createdAt: { bsonType: "date" },
tags: {
bsonType: "array",
items: { bsonType: "string" }
}
}
}
}
});
appDb.article_demo.createIndex(
{ slug: 1 },
{ name: "uk_article_slug", unique: true }
);
appDb.article_demo.createIndex(
{ owner: 1, status: 1, createdAt: -1 },
{ name: "idx_owner_status_createdAt" }
);
appDb.article_demo.createIndex(
{ createdAt: 1 },
{ name: "ttl_article_demo_createdAt_7d", expireAfterSeconds: 604800 }
);schema validation 不等于业务模型设计,但它能阻止最典型的测试脏数据:字段缺失、类型乱跳、日期字符串和无 owner 数据。
连接串
本地直连:
mongodb://te_app:YOUR_APP_PASSWORD@127.0.0.1:27018/te_app?authSource=te_app&directConnection=trueReplica Set:
mongodb://te_app:YOUR_APP_PASSWORD@mongo1:27017,mongo2:27017,mongo3:27017/te_app?authSource=te_app&replicaSet=rs0Atlas SRV:
mongodb+srv://te_app:YOUR_APP_PASSWORD@YOUR_CLUSTER_HOST/te_app?authSource=admin不要把密码直接写进 application.yml、.env 之外的代码、文档截图或日志。团队模板里保留占位符,真实值走 secret manager、CI secret 或本机私有 .env。
角色和权限
| 账号 | 用途 | 权限边界 |
|---|---|---|
| root | 初始化、紧急救援 | 不给普通开发长期使用 |
| app | 应用读写 | 只读写项目 database |
| readonly | Compass / 排障 / 查询 | 只读项目 database |
| ops | 备份、恢复、索引治理 | 明确脚本和审计范围 |
| ci | 初始化、冒烟、清理 | 只操作测试前缀 |
共享环境至少做三类验收:
app 账号能写入和查询 te_app.article_demo。readonly 账号查询成功,但插入、删除、建索引必须失败。root 账号不进入应用配置和 Compass 默认连接。
用正反实验闭合一次写入
这组命令把连接、写入、索引命中、权限拒绝和清理串成一条证据链。若环境来自旧 volume,先回到初始化日志和用户列表排查,不要临时提升业务账号权限。
设置变量:
export MONGODB_URL="mongodb://te_app:YOUR_APP_PASSWORD@127.0.0.1:27018/te_app?authSource=te_app&directConnection=true"
export MONGODB_READONLY_URL="mongodb://te_readonly:YOUR_READONLY_PASSWORD@127.0.0.1:27018/te_app?authSource=te_app&directConnection=true"连接和 ping:
mongosh "${MONGODB_URL}" --eval "db.runCommand({ ping: 1 })"成功时输出包含 ok: 1;若出现 Authentication failed,先核对用户实际创建在哪个 database,再核对连接串的 authSource。
写入:
mongosh "${MONGODB_URL}" --eval '
db.article_demo.insertOne({
slug: "mongodb-tool-efficiency",
title: "MongoDB 工具效率验证",
status: "draft",
owner: "tool-efficiency",
createdAt: new Date(),
tags: ["mongodb", "tool"]
})
'查询:
mongosh "${MONGODB_URL}" --eval '
db.article_demo.find(
{ owner: "tool-efficiency", status: "draft" },
{ _id: 0, slug: 1, title: 1, status: 1 }
).toArray()
'查看索引:
mongosh "${MONGODB_URL}" --eval 'db.article_demo.getIndexes()'查看执行计划:
mongosh "${MONGODB_URL}" --eval '
db.article_demo
.find({ owner: "tool-efficiency", status: "draft" })
.sort({ createdAt: -1 })
.explain("executionStats")
'在这组数据和复合索引下,queryPlanner.winningPlan 应出现 IXSCAN,indexName 应为 idx_owner_status_createdAt,而不是 COLLSCAN。小样本的耗时没有比较价值,稳定证据是执行计划选择了预期索引,且 totalDocsExamined 不应随 collection 总量无关增长。
低权限反向验证:
mongosh "${MONGODB_READONLY_URL}" --eval '
db.article_demo.findOne({ owner: "tool-efficiency" })
'
mongosh "${MONGODB_READONLY_URL}" --eval '
db.article_demo.insertOne({
slug: "should-fail",
title: "readonly should fail",
status: "draft",
owner: "tool-efficiency",
createdAt: new Date()
})
'第一条读取应成功,第二条写入应返回未授权错误。把错误码、账号、database、collection 和时间一起留作权限证据;如果写入反而成功,说明连接串指向了错误用户,或该用户被授予了超出 read 的角色。
清理单条测试数据:
mongosh "${MONGODB_URL}" --eval '
db.article_demo.deleteMany({ owner: "tool-efficiency" })
'如果要清理整个个人环境,用 Compose down -v;共享环境必须走 dry-run。
项目里建议保留:
dev-dependencies/mongodb/
compose.yaml
.env.example
init/
scripts/
docs/dependency-setup.md
src/main/resources/application-local.ymlSpring Boot 示例:
spring:
data:
mongodb:
uri: ${MONGODB_URI}本地 .env:
MONGODB_URI=mongodb://te_app:YOUR_APP_PASSWORD@127.0.0.1:27018/te_app?authSource=te_app&directConnection=trueNode.js 示例:
import { MongoClient } from "mongodb";
const client = new MongoClient(process.env.MONGODB_URI, {
appName: "tool-efficiency-local"
});
await client.connect();
const db = client.db("te_app");
await db.collection("article_demo").insertOne({
slug: "node-client-verify",
title: "Node client verify",
status: "draft",
owner: "tool-efficiency",
createdAt: new Date()
});
await client.close();接入原则:
连接串只来自环境变量或 secret,不写死在代码。应用账号不使用 root。database、collection、index 名称进入迁移脚本或初始化脚本,不靠手工点 GUI。
本地 profile 默认连 127.0.0.1:27018,共享环境必须显式命名。生产连接必须经过只读查询、变更审批和日志脱敏,不让开发机默认保存。
MongoDB 解决的核心架构问题
MongoDB 的价值不是“字段不用提前建表”。架构师要看的是:
文档聚合边界:把经常一起读写的数据放在一个文档里,减少跨表 join 和跨服务拼装。灵活 schema:迭代快,但必须用 validation、写入 DTO、索引和审计把自由度关进笼子。水平扩展:Sharded Cluster 可以扩容量和吞吐,但 shard key 选错会把复杂度放大。
高可用:Replica Set 提供自动选主和数据冗余,但有复制延迟、写入确认和故障切换窗口。工具链:mongosh、Compass、Database Tools、profiler、explain 能让团队快速验证、排障和治理。
什么时候不该优先选 MongoDB:
强关系、复杂 join、跨实体事务是核心路径。数据模型已经稳定,并且团队 SQL、关系型治理能力成熟。业务需要复杂报表、OLAP 聚合和列式扫描。
团队没有能力治理 schema、索引、备份、权限和数据生命周期。
主流生产架构
单机架构
组成:一个 mongod,一个数据目录,一个端口。
优点:简单、成本低、适合开发和小型非核心系统。
问题:单点故障、容量受限、没有自动故障切换、备份恢复窗口压力大。
适用:个人开发、本地测试、一次性数据处理、低风险内部工具。
Replica Set
组成:多个 mongod 成员,一个 primary,多个 secondary,可选 arbiter。
核心能力:
primary 负责写入,secondary 复制 oplog。primary 故障后自动选举。可以按 read preference 把部分读流量导向 secondary。
支持更完整的事务、change streams 和 retryable writes 场景。
核心问题:
复制延迟导致读到旧数据。writeConcern 配置过低会增加丢写风险,过高会增加延迟。选举期间写入会短暂失败,客户端必须有重试策略。
keyfile / X.509、hostname、时间同步、oplog 大小和备份都要治理。
适用:绝大多数生产 MongoDB 自建基础形态。
Sharded Cluster
组成:mongos 路由、config server replica set、多组 shard replica set、balancer。
核心能力:
按 shard key 水平拆分数据。扩展容量和写入吞吐。分片迁移由 balancer 管理。
核心问题:
shard key 选错会导致热点分片、jumbo chunk、跨分片查询和扩容困难。查询如果没有 shard key,可能 scatter-gather 到多个 shard。事务、聚合、排序、分页和唯一索引都要按分片边界重新评审。
运维组件多,备份恢复和升级比 Replica Set 复杂得多。
适用:单 Replica Set 容量、写入或存储明显成为瓶颈,并且团队有分片治理能力。
Atlas / 托管服务
托管服务适合降低备份、升级、监控、网络和故障处理复杂度,但不是“没有运维”。架构师仍要确认:
版本和升级策略。网络边界、IP allowlist、PrivateLink / VPC Peering。备份保留、恢复时间、PITR 和演练。
审计、加密、密钥管理和合规。费用、数据导出、退出方案和云厂商绑定。
Kubernetes Operator
Operator 适合平台团队把 MongoDB 作为内部服务交付。它不适合单个业务项目临时“图方便”部署数据库。
必须确认:
CRD 和 Operator 版本。StorageClass、PVC、拓扑、节点亲和和备份。Secret、证书、keyfile、滚动升级。
故障恢复和升级回滚由谁负责。
核心机制
BSON 与文档模型
MongoDB 存的是 BSON 文档,不是纯 JSON 文本。字段类型、日期、ObjectId、数组、嵌套对象都会影响查询、索引和序列化。
架构师要关心:
ObjectId 含时间信息,但不是业务全局 ID 方案的万能替代。日期要统一存 Date,不要在同一字段混用字符串和时间类型。数组字段索引会变成 multikey index,要评估基数和查询方式。
文档大小、嵌套深度和频繁增长字段会影响性能和迁移。
WiredTiger、journal 和 cache
WiredTiger 是现代 MongoDB 的默认存储引擎。性能问题常见根源不是“MongoDB 慢”,而是 cache、磁盘、索引、锁和工作集不匹配。
检查入口:
db.serverStatus().wiredTiger.cache
db.serverStatus().connections
db.serverStatus().opcounters判断:
工作集大于可用 cache,读会频繁打到磁盘。索引过多会放大写入成本。慢查询、排序和聚合可能消耗大量内存。
Docker Desktop 给的内存太小会制造假性能问题。
oplog、readConcern 和 writeConcern
Replica Set 依赖 oplog 复制。写入确认和读取一致性要显式评审:
| 配置 | 常见用途 | 风险 |
|---|---|---|
writeConcern: { w: 1 } | 低延迟写入 | primary 故障时存在丢失风险 |
writeConcern: { w: "majority" } | 更强持久性 | 写入延迟增加 |
readConcern: "local" | 普通低延迟读取 | 可能读到未 majority 提交数据 |
readConcern: "majority" | 更强一致性读取 | 延迟和资源成本增加 |
一致性不能停在口号上。项目模板至少要说明默认 concern、业务关键路径、重试策略和读写分离边界。
索引体系
MongoDB 索引不是“慢了再建”。团队模板至少定义:
唯一约束用 unique index,而不是只在代码里查重。高频查询必须有 compound index,字段顺序按等值、排序、范围综合判断。TTL index 只用于可过期数据,不用于替代业务删除审计。
text index 适合轻量文本搜索,不替代 Elasticsearch / OpenSearch。大 collection 建索引要评估时间、锁、磁盘和回滚。
Shard key
分片键是 Sharded Cluster 的架构生命线。
好的 shard key 要同时考虑:
高基数。写入分布均衡。查询能命中 shard key。
不频繁更新。能支持未来扩容。
坏 shard key 的现象:
一个 shard 长期热点。chunk 迁移频繁或失败。查询大量 scatter-gather。
分片后唯一约束、事务和分页变复杂。
查看数据库:
show dbs
use te_app
show collections查看当前连接身份:
db.runCommand({ connectionStatus: 1 })查看 collection 统计:
db.article_demo.stats()查看索引:
db.article_demo.getIndexes()查看慢操作:
db.aggregate([{ $currentOp: { allUsers: false } }, { $match: { active: true } }])开启 profiling 到慢查询阈值:
db.setProfilingLevel(1, { slowms: 100 })
db.system.profile.find().sort({ ts: -1 }).limit(5)导出测试库:
mongodump --uri "${MONGODB_URL}" --out ./tmp/mongodb-dump恢复到临时库:
mongorestore --uri "${MONGODB_URL}" \
--nsFrom "te_app.*" \
--nsTo "te_app_restore.*" \
./tmp/mongodb-dump受控 dry-run 清理建议先列对象:
mongosh "${MONGODB_URL}" --eval '
db.getCollectionNames()
.filter(name => name.startsWith("article_"))
.forEach(name => print(`would clean collection: ${name}`))
'连接被拒绝
现象:ECONNREFUSED 或 connection refused。
判断:
docker ps --filter name=your-project-mongodb
docker logs your-project-mongodb --tail 100
netstat -ano | findstr ":27018"常见原因:容器没启动、端口被占用、宿主端口写错、服务只绑定容器内部。
Authentication failed
现象:MongoServerError: Authentication failed。
判断:
mongosh "mongodb://root:YOUR_STRONG_ROOT_PASSWORD@127.0.0.1:27018/admin?directConnection=true" \
--eval "db.runCommand({ connectionStatus: 1 })"常见原因:
用户创建在 admin,连接串却用业务库做 authSource。初始化脚本没有执行,业务用户不存在。旧 volume 里保留了旧密码。
密码里有特殊字符但连接串没有 URL encode。
初始化变量不生效
现象:改了 MONGO_INITDB_ROOT_USERNAME 或 init 脚本,重启后仍是旧用户。
原因:官方镜像初始化逻辑只在空数据目录首次执行。旧 /data/db volume 已经存在时,不会重新跑初始化脚本。
修复:
个人环境:docker compose down -v 后重启。共享环境:不要删 volume,走用户创建、密码轮换和变更记录。
Schema validation 拒绝写入
现象:插入时报 Document failed validation。
判断:
db.getCollectionInfos({ name: "article_demo" })修复:确认字段名、类型、必填项和日期类型。不要为了临时测试把 validation 改成空规则,应该修正测试数据。
查询慢
现象:小数据能跑,大数据慢,CPU 或磁盘升高。
判断:
db.article_demo.find({ owner: "tool-efficiency" }).explain("executionStats")
db.article_demo.getIndexes()关注:是否 COLLSCAN、是否排序未走索引、返回文档数是否过大、索引字段顺序是否错。
Duplicate key
现象:E11000 duplicate key error。
原因:unique index 生效了。修复路径不是删索引,而是确认业务唯一键、幂等写入和测试数据清理。
Replica Set 连接不稳定
现象:驱动反复发现节点失败、primary not found、server selection timeout。
判断:连接串是否带 replicaSet;成员 hostname 是否能从客户端解析;是否误用了容器内部 hostname;keyfile 是否导致成员认证失败。
Compass 连错环境
现象:GUI 里看到生产库,或者保存了含密码的生产连接。
修复:连接命名必须带环境和权限,例如 local-te-app-readwrite、shared-te-app-readonly;生产只读连接单独审批,禁止默认打开写权限。
MongoDB 凭证治理重点:
连接串不进 Git。root 密码不进入应用配置。Compass 连接模板默认只读。
Atlas SRV 地址、用户名、密码和证书都算敏感信息。TLS 私钥、keyfile、备份 dump 不进仓库。日志打印连接串时必须脱敏。
敏感信息扫描:
rg -n "mongodb(\\+srv)?://|MONGO_INITDB_ROOT_PASSWORD|MONGODB_.*PASSWORD|BEGIN .*PRIVATE KEY|keyFile|authSource=.*admin" .代理与网络:
本地直连不需要 HTTP 代理。Atlas / 托管服务要确认 IP allowlist、VPC / PrivateLink、DNS 和 TLS。CI 环境如果无法访问托管服务,不要临时开放 0.0.0.0/0,应使用临时 allowlist 或内网 runner。
Docker Desktop 网络和公司 VPN 叠加时,优先用 127.0.0.1 本机端口验证,再排查 DNS 和路由。
团队模板至少沉淀:
dev-dependencies/mongodb/compose.yaml。.env.example,只保留占位符。init/ 初始化用户、collection、validation、index。
scripts/verify.sh,验证 ping、写入、查询、索引和低权限。scripts/clean-dry-run.sh 和 scripts/clean-confirmed.sh。docs/dependency-setup.md,写明端口、账号、连接串、Compass 连接名和清理方式。
docs/data-boundary.md,写明哪些数据不能导出、不能进入 dump、不能放进截图。
团队职责:
| 角色 | 负责 |
|---|---|
| 平台 / 架构 owner | 版本、模板、权限、备份恢复和共享实例策略 |
| 后端 owner | collection、schema validation、索引、迁移脚本和连接池 |
| 测试 owner | 测试数据、清理脚本、脱敏 dump 和回归验证 |
| 安全 owner | 凭证、TLS、审计、生产连接和数据导出 |
| 新成员 | 按 README 跑通本地验证,不复用生产连接 |
无认证实例暴露是最高优先级风险
Docker Official Image mongo 默认配置不要求认证。个人本地只绑定 127.0.0.1,共享环境必须启用认证、强密码、最小权限、网络隔离和审计。判断标准:mongosh mongodb://HOST:27017 不带账号不能成功列库。
旧 volume 会保留旧世界
初始化变量和 /docker-entrypoint-initdb.d 脚本只在空数据目录首次生效。改 .env 后不生效时,先查 volume,而不是怀疑 Docker。个人可 down -v,共享环境必须走迁移脚本和变更记录。
authSource 是连接失败的高频根因
root 在 admin,业务用户可能在业务库。连接串里 authSource 写错会让账号看起来“明明存在却登录失败”。团队 README 必须写清每个账号在哪个库创建。
schema 自由不是没有治理
MongoDB 的灵活 schema 如果没有 validation、DTO、写入脚本和审查,很快会出现同一字段有字符串、数字、日期三种类型。判断标准:新增 collection 必须有字段边界、索引和清理策略。
索引不是慢了再补
慢查询治理要从 explain("executionStats")、profiler 和实际查询模式出发。不能只看“有索引”。判断标准:高频查询避免 COLLSCAN,排序字段和过滤字段匹配 compound index。
Replica Set 不是只多起两个容器
复制集要治理 keyfile / X.509、hostname、oplog、选举、writeConcern、readPreference、客户端重试和备份。只写三个容器启动命令,不算完成高可用架构说明。
分片不能提前炫技
Sharded Cluster 能扩展容量,也会引入 shard key、chunk、balancer、跨分片事务、唯一索引和备份复杂度。判断标准:没有容量曲线、查询模式和 shard key 评审,不进入分片。
Compass 是生产事故入口
GUI 提效也放大误操作。生产连接默认只读,连接名带环境,禁止保存 root。导出数据要脱敏,截图不能露出真实库名、用户数据和连接串。
备份不是 dump 文件存在
mongodump 是工具,不是备份体系。至少要恢复到临时库,验证文档数、索引、validation、权限和应用查询。采用 PITR、oplog、Atlas continuous backup 或云厂商备份时,应按实际版本和服务层级确认保留窗口、恢复粒度、费用与限制,并用演练结果证明 RPO / RTO。
数据生命周期要前置
TTL index 适合过期测试数据和可自动删除的数据,不适合替代审计和合规删除流程。共享环境必须有 owner、保留期、dry-run 清理和恢复路径。
上线与移交核对
本机跑通:
Docker / Compose 可用。宿主端口 27018 未冲突。使用固定 mongo 版本,不使用 latest。
root 密码为占位变量,真实值不入库。mongosh 能 ping。app 账号能写入、查询和删除自己的测试数据。
readonly 账号写入失败。collection 有 schema validation。高频查询有索引和 explain 证据。
测试数据可按 owner 清理。
项目接入:
连接串来自环境变量或 secret。authSource 与用户所在库一致。应用账号不是 root。
初始化脚本进入项目模板。Compass 连接默认只读。CI 或本地脚本能验证连接和权限。
架构判断:
单机、Replica Set、Sharded Cluster、Atlas / 托管边界已写清。Replica Set 评审 keyfile / X.509、oplog、writeConcern、故障切换和备份。分片前有 shard key、容量、查询模式和扩容评审。
备份恢复至少演练一次。版本、驱动、Compass、Database Tools 维护责任明确。
安全治理:
无认证连接不能访问共享实例。生产连接不保存在普通开发 GUI。备份 dump、keyfile、TLS 私钥不进仓库。
日志和截图不暴露连接串。清理脚本有 dry-run、环境确认和 owner 过滤。
