MongoDB Compass 与 mongosh 文档库客户端工具手册
“密码没错”为什么还是连不上
线上订单查询变慢,开发者从密码管理器复制 Atlas URI 到 Compass,却得到认证失败;换成管理员账号后连接成功,于是排障继续。真正的问题可能只是 URI path 指向业务库,而用户创建在 admin,缺少 authSource=admin。管理员账号虽然绕过了现象,却把 Documents、Indexes、Import 和 Drop 等写入口一起交给了排障客户端。
Compass 适合观察 collection、索引、schema 样本和查询计划;mongosh 适合把同一连接与诊断步骤固化成可复跑证据。它们最终都通过 MongoDB Driver 完成 URI 解析、DNS、TLS、认证、拓扑发现和 server selection。图形界面的 readOnly 开关是防误触提示,服务端 role 才是权限边界。
操作基线采用 Compass 1.49.12 与 mongosh 2.9.2。mongosh 当前安装文档支持连接 MongoDB 7.0 或更高版本;Compass 的具体能力还受服务端版本、部署形态与功能影响,不能把 GUI 能建立连接等同于全部功能受支持。官方独立 mongosh 二进制包含受支持的 Node.js 运行时;通过 npm 或 npx 使用时则依赖外部 Node.js,运行时过旧会收到警告。共享团队应优先使用 MongoDB Download Center 的签名安装包并验证完整性,记录实际 mongosh --version,再按 Compass release notes 和 mongosh release notes 做升级回归。
安装后先验证本机入口
Compass 从 Download and Install Compass 选择对应操作系统。Linux 需要受支持的 64 位发行版;Compass 不支持虚拟桌面环境,使用 Nvidia 图形设备遇到空白窗口时可用 --disable-gpu 辅助判断渲染问题,但这不会修复数据库连接。Readonly Edition 已进入弃用路径,长期方案是标准 Compass、服务端 read role 与 Compass readOnly 设置组合。
mongosh 从 Install mongosh 安装,随后执行:
mongosh --version
mongosh --build-info版本输出应能进入资产清单;--build-info 可辅助区分独立二进制与包装环境。不要把本机已有的旧 mongo shell 当成 mongosh,两者的 JavaScript 运行时、错误语义和帮助方法并不相同。
最安全的首次连接不把密码写进 URI。把 --password 放在最后且不给值,mongosh 会遮罩提示输入:
mongosh "mongodb://mongo-dev.example.test:27017/app?authSource=admin&tls=true" \
--username app_reader --password若使用企业 CA,增加 --tlsCAFile ./certs/company-ca.pem;需要客户端证书时使用 --tlsCertificateKeyFile。--tlsAllowInvalidCertificates 和 --tlsAllowInvalidHostnames 会拆掉身份验证,最多用于隔离实验,不能写进收藏连接、脚本或共享文档。
把 URI 拆回可判断的字段
一个典型 URI 如下:
mongodb://app_reader@mongo-a.example.test:27017,mongo-b.example.test:27017/app?authSource=admin&replicaSet=rs0&tls=true&readPreference=primaryPreferred&serverSelectionTimeoutMS=5000scheme 为 mongodb:// 时,host 列表是拓扑发现的 seed,不保证后续请求永远发送到第一个地址;驱动握手后会学习 replica set 成员。mongodb+srv:// 通过 DNS SRV 取得 seed,并读取 TXT 中允许的连接选项,默认启用 TLS。SRV 连接失败要分别验证 DNS SRV/TXT、目标端口、Atlas IP Access List 和证书,不能只 ping URI 中看见的域名。
URI path /app 选择连接后的默认数据库。authSource=admin 指用户凭据在哪个数据库验证;两者可以不同。省略 authSource 时会按 URI 与机制规则推导,迁移旧连接或复制 Atlas URI 时最容易因此产生“同一密码时好时坏”。密码含 @、:、/、? 等保留字符时必须百分号编码,否则解析后的 host、path 和 query 都可能改变。
replicaSet=rs0 要求发现到的成员属于该集合,并启用 replica set 语义;名称写错通常表现为 server selection 超时。directConnection=true 强制把单个目标当作直接连接,适合诊断某个节点或本地端口映射,但会绕过正常发现与故障切换,不能成为生产副本集的默认修复。容器只映射一个成员时,发现到的其他内部主机不可达也是常见失败,正确修复是网络与公布地址,不是永久 direct connection。
readPreference 决定哪些成员可以承担读取。secondaryPreferred 可能读到复制延迟数据,也可能在 secondary 不可用时回退 primary;primaryPreferred 同样不是“永远只读 primary”。故障排查记录必须带上 read preference 与实际 server,不能用一次旧数据证明应用写入丢失。serverSelectionTimeoutMS 限制驱动挑选可用 server 的等待时间;过短会把短暂 DNS/选举抖动放大,过长会拖慢失败反馈,应从应用 SLO 和网络基线推导。
Compass 的 Advanced Connection Options 可填写认证机制、TLS、Proxy/SSH 与更多 driver 选项。SSH tunnel 把本地客户端流量经堡垒机转发,不会自动授予数据库权限;堡垒机私钥、known hosts 校验和会话审计仍需独立治理。SOCKS5 代理改变网络路径,也可能改变 DNS 解析位置。Atlas 的 IP Access List 是网络准入,不是数据库授权,0.0.0.0/0 加强密码并不能形成等价保护。
在本地做只读正反实验
下面的固定密码和端口仅供一次性本地容器。先启动 MongoDB 8.0 实验实例:
docker run -d --name mongodb-client-lab \
-p 127.0.0.1:27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=root \
-e MONGO_INITDB_ROOT_PASSWORD=root-lab-only \
mongo:8.0等待日志出现可接受连接后,由本地 root 初始化样本与只读用户:
docker exec mongodb-client-lab mongosh \
--quiet --username root --password root-lab-only \
--authenticationDatabase admin --eval '
const app = db.getSiblingDB("client_lab");
app.orders.insertMany([
{ orderNo: "A1001", status: "PAID", amount: 99, createdAt: new Date() },
{ orderNo: "A1002", status: "PENDING", amount: 42, createdAt: new Date() }
]);
app.orders.createIndex({ status: 1, createdAt: -1 });
app.createUser({
user: "client_reader",
pwd: "reader-lab-only",
roles: [{ role: "read", db: "client_lab" }]
});
'固定密码出现在进程参数中,所以这个方式只用于隔离实验。共享环境应使用交互提示、短期身份或密钥注入。接着以只读用户连接,验证身份、默认 DB 与受限查询:
docker exec mongodb-client-lab mongosh \
'mongodb://client_reader:reader-lab-only@127.0.0.1:27017/client_lab?authSource=client_lab' \
--quiet --eval '
printjson(db.runCommand({ connectionStatus: 1, showPrivileges: false }));
print(db.getName());
printjson(db.orders.find(
{ status: "PAID" },
{ _id: 0, orderNo: 1, status: 1 }
).limit(5).toArray());
'预期认证用户是 client_reader@client_lab,默认 DB 为 client_lab,结果只含 A1001 的投影字段。Compass 使用同一 URI 时,在 Documents 页输入 { status: "PAID" },Project 输入 { _id: 0, orderNo: 1, status: 1 },Limit 设为 5,应得到一致结果。
现在故意把认证库写错:
docker exec mongodb-client-lab mongosh \
'mongodb://client_reader:reader-lab-only@127.0.0.1:27017/client_lab?authSource=admin' \
--quiet --eval 'db.runCommand({ ping: 1 })'预期出现 Authentication failed。这证明 default DB 与 authSource 是两条独立语义,不需要重置密码。再恢复正确 URI,尝试写入与建索引:
docker exec mongodb-client-lab mongosh \
'mongodb://client_reader:reader-lab-only@127.0.0.1:27017/client_lab?authSource=client_lab' \
--quiet --eval 'db.orders.insertOne({ orderNo: "A1003", status: "NEW" })'
docker exec mongodb-client-lab mongosh \
'mongodb://client_reader:reader-lab-only@127.0.0.1:27017/client_lab?authSource=client_lab' \
--quiet --eval 'db.orders.createIndex({ orderNo: 1 })'两条命令都应以 not authorized 失败。MongoDB 内置 read role 可以查看文档、索引和查询计划;创建文档、collection 或索引需要额外权限。Compass readOnly 开关若关闭,上述服务端拒绝仍应成立,这才是可以进入生产的证据。
用 Explain 把“慢”变成证据
先只取 planner,不执行完整结果集:
db.orders.find({ status: "PAID" })
.sort({ createdAt: -1 })
.limit(20)
.explain("queryPlanner")对本地样本,winning plan 应能使用 { status: 1, createdAt: -1 } 索引。确认过滤条件和数据规模可控后,再运行:
db.orders.find({ status: "PAID" })
.sort({ createdAt: -1 })
.limit(20)
.explain("executionStats")重点比较 nReturned、totalKeysExamined、totalDocsExamined、执行阶段和排序方式。小样本中的绝对耗时没有生产代表性;更稳定的判断是候选查询在相同数据分布下,扫描文档与返回文档之比是否下降、是否避免阻塞排序、计划在升级前后是否保持预期形态。
为了观察反例,在实验库查询没有索引的 amount:
db.orders.find({ amount: { $gte: 40 } })
.limit(20)
.explain("executionStats")预期计划出现 COLLSCAN。这里只有两条文档,所以它不慢,却已经暴露机制:匹配前需要检查 collection 文档。生产上不能为了让 Compass 图表变绿就直接创建索引;索引会增加写放大、内存与磁盘占用、构建时间和复制压力。应先用真实查询形状、基数、排序、更新频率与现有复合索引评审,再通过数据库变更流程上线和回滚。
Compass 的 Query Performance 页面和 mongosh explain() 使用同一服务端计划能力。executionStats 会执行候选查询,聚合还可能消耗大量 CPU、内存或临时磁盘;在大 collection 上先用 queryPlanner、窄过滤和 limit,不能把 Explain 当成无成本静态分析。
把诊断脚本接进项目
仓库可以保存一个无凭据只读探针,URI 由本机或 CI secret 注入:
// scripts/db/mongodb-readonly-check.js
const expectedDb = process.env.MONGODB_EXPECTED_DB;
if (!expectedDb) throw new Error("missing MONGODB_EXPECTED_DB");
const status = db.runCommand({ connectionStatus: 1, showPrivileges: false });
if (status.ok !== 1) throw new Error("connectionStatus failed");
if (db.getName() !== expectedDb) {
throw new Error(`unexpected database: ${db.getName()}`);
}
const sample = db.getCollection("orders")
.find({}, { _id: 1 })
.limit(1)
.toArray();
print(EJSON.stringify({
ok: 1,
db: db.getName(),
authenticatedUsers: status.authInfo.authenticatedUsers,
sampleCount: sample.length
}));运行入口如下:
export MONGODB_URI='mongodb://app_reader@mongo-dev.example.test:27017/app?authSource=admin&tls=true'
export MONGODB_EXPECTED_DB='app'
mongosh "$MONGODB_URI" --username app_reader --password \
--quiet --file scripts/db/mongodb-readonly-check.js
unset MONGODB_URI MONGODB_EXPECTED_DB预期输出包含 ok:1、正确数据库与认证身份。sampleCount 可以是 0,空 collection 不应让健康检查误报。流水线还应在隔离测试库执行一次预期失败的写入,并断言错误码属于未授权;不要在生产集合插入“测试文档”证明只读。
应用 driver 和客户端应复用同一套 URI 字段定义、CA 来源与认证机制,但不能共用管理员凭证。mongosh 成功只证明当前执行环境可连接,不证明应用 Pod 的 DNS、代理、连接池、超时、read concern、write concern 和 codec 正确。把探针放进与应用相同的网络环境运行,才能区分开发机通路和运行时通路。
历史、日志与智能功能也是数据出口
mongosh 默认会写日志,并把交互命令保存在用户配置目录。可以在 shell 中查看和设置:
config.get("redactHistory")
config.set("redactHistory", "remove-redact")
config.set("disableLogging", true)
config.set("disableSchemaSampling", true)
config.set("enableTelemetry", false)redactHistory=remove 会移除 db.auth()、connect() 等敏感命令,remove-redact 还会遮蔽部分路径、邮箱和 URL;它不是通用数据防泄漏器,业务查询中的手机号、令牌或身份证仍不应直接输入交互历史。disableSchemaSampling 会减少自动补全为了解 schema 而进行的采样,也会降低补全体验。关闭日志和 telemetry 是隐私选择,不替代服务端审计。
Compass 的收藏连接、查询历史、日志、schema 分析、导出文件和截图都可能泄露 host、库名、collection、字段名与样本值。启用自然语言查询或其他 AI 功能前,应检查产品的数据使用设置、组织政策和目标数据等级;敏感库默认关闭,不能因为功能集成在官方客户端里就自动获得数据出境批准。
凭据保存依赖操作系统密钥能力和本机用户边界。共享跳板机上多个操作者复用同一 OS 账号,会让收藏连接、历史和审计主体混在一起。更稳妥的方式是每人独立身份、短期数据库凭证、设备合规和集中回收,不在团队 Wiki 放可直接连接的完整 URI。
导入导出不是备份恢复
Compass 可对单个 collection 导入或导出 JSON/CSV,适合受控小样本和开发数据准备。CSV 无法无损表达所有 BSON 类型、嵌套对象和数组;普通 JSON 也可能丢失 ObjectId、Date、Decimal128、长整型和二进制语义。导出前应固定 filter、projection、最大行数和 Extended JSON 形式,导出后在隔离库回读验证类型,而不是只比较文件行数。
导出执行期间数据仍可能变化,多个 collection 之间没有自动一致性边界,用户、角色、索引和集群元数据也不会因此完整归档。正式备份使用 Atlas 备份或与部署形态匹配的 Database Tools 与恢复演练。mongodump 也不是“拿到一个文件就安全”,仍需考虑版本兼容、一致性、oplog、加密、容量和恢复时间目标。
样本文件的治理链应包含申请人、字段、过滤条件、行数、存储位置、接收者、过期时间和销毁证据。把生产 JSON 拖进个人下载目录再导入本地 MongoDB,会生成第二份缺少审计、备份和删除策略的数据资产。
批量删除先证明边界,再执行写入
Compass 1.42.0 起提供 Bulk delete,并会把 Query Bar 的过滤条件带入 Delete Documents 窗口展示 Preview;查询栏留空会匹配整个 collection。Preview 只能帮助核对样本,不是回滚机制。生产删除应先由数据 owner 固化 tenant、业务批次、时间边界、批准上限与恢复来源,再用完全相同的过滤条件留下只读证据:
const cleanupBatchId = process.env.CLEANUP_BATCH_ID;
if (!cleanupBatchId || !process.env.CLEANUP_CUTOFF_ISO) {
throw new Error("CLEANUP_BATCH_ID and CLEANUP_CUTOFF_ISO are required");
}
const cutoff = ISODate(process.env.CLEANUP_CUTOFF_ISO);
const filter = {
tenantId: "tenant-demo",
cleanupBatchId,
expiresAt: { $lt: cutoff }
};
db.sessions.find(filter, { _id: 1, tenantId: 1, expiresAt: 1 })
.limit(20)
.toArray();
db.sessions.countDocuments(filter, { maxTimeMS: 5000 });
db.sessions.find(filter).limit(1).explain("queryPlanner");预览字段和计数必须与工单一致,查询计划还要命中已评审索引。满足这些条件后,再由短期提权身份执行带超时和索引提示的删除,并立即核对残留:
const result = db.sessions.deleteMany(filter, {
hint: { tenantId: 1, cleanupBatchId: 1, expiresAt: 1 },
maxTimeMS: 30000,
writeConcern: { w: "majority", wtimeout: 10000 }
});
printjson(result);
printjson({ remaining: db.sessions.countDocuments(filter, { maxTimeMS: 5000 }) });deletedCount 是已确认删除量,remaining=0 才说明当前查询已无匹配项;两者都不能证明数据可恢复。primary 在操作中途故障时,尚未处理的文档不会自动删除;分片 collection 在事务外跨分片广播,遇到并发 chunk migration 还可能残留匹配文档。应按审批决定迭代执行到零、避开 balancing window,或承担分布式事务的成本,不能看到一次 acknowledged 就宣布完成。
大规模清空 collection 时,逐文档删除会为每个成功删除写 oplog,可能比 drop 后重建更慢;但 drop 会同时移除索引,分片 collection 还要重新分片。没有备份或可重建来源、明确批次边界和停止条件时,不应执行批量删除。
沿握手阶段定位连接故障
无法解析 mongodb+srv 时先查 SRV/TXT 与企业 DNS;能解析但 server selection 超时,再查目标端口、Atlas IP Access List、VPN、代理和 replica set 成员可达性。只有单个 seed 可达时,驱动仍可能因为发现到其他不可达成员而失败。directConnection=true 能辅助证明这一点,却不是长期拓扑配置。
TLS 报 unknown CA、hostname mismatch 或 client certificate required,分别核对 CA 链、URI host 与证书 SAN、客户端证书。SSH 能登录但 MongoDB 连接失败时,还要检查 tunnel 的目标 host 是从堡垒机视角解析、local bind 是否冲突,以及 Atlas 是否允许最终出口地址。
认证失败时按 username、密码编码、authSource、authMechanism 顺序拆解。已认证却看不到 collection,使用 connectionStatus 核对身份,再检查 role 的数据库、默认 DB、view 权限与 read preference。不要用 show dbs 是否完整判断授权,因为数据库可见性也受权限和内容影响。
查询卡住时先在另一会话确认 server selection 与当前操作,再判断是无索引扫描、排序、锁等待、网络回包还是 Compass 本地渲染。GUI 一次加载大文档、数组或二进制值会消耗客户端内存;projection 和 limit 既保护数据库,也保护桌面进程。停止 GUI 请求不一定立即取消服务端操作,必要时由有权限的 owner 查看当前操作并按变更流程终止。
清理实验和撤销访问
本地实验先在服务端删除用户与样本库,再删除容器:
docker exec mongodb-client-lab mongosh \
--quiet --username root --password root-lab-only \
--authenticationDatabase admin --eval '
const app = db.getSiblingDB("client_lab");
app.dropUser("client_reader");
app.dropDatabase();
'
docker rm -f mongodb-client-lab若实验用了 named volume,还要在确认无需恢复后单独删除 volume。Compass 侧删除收藏连接、清理历史与导出样本,再从操作系统凭据存储回收条目;mongosh 侧按组织策略清理 history、日志和临时脚本。只卸载客户端不会自动撤销 Atlas database user、IP Access List、X.509 证书或 SSH key,服务端访问必须由 owner 显式回收并验证旧凭据失败。
错误索引的回滚不是看到磁盘上涨就立即 dropIndex()。先确认查询没有依赖、隐藏或移除是否适用于当前发布流程、secondary 与复制压力是否稳定,再在变更窗口操作。数据导入的回滚则需要导入批次标识、幂等键或独立临时 collection;没有可识别边界的覆盖导入通常无法可靠撤销。
从个人工具走向长期治理
一次性查询、自动化探针和精确故障证据优先使用 mongosh;需要探索文档结构、比较索引、构建聚合和向同事展示计划时使用 Compass。Atlas UI 更适合托管集群、备份与平台指标,Compass 适合数据面探索,两者权限不要用同一个高权角色打通。受监管环境若禁止桌面保存数据,可在批准的跳板环境使用 mongosh 和审计脚本,而不是把 Compass 放进不受支持的虚拟桌面后默认它安全。
容量预算要同时看 collection 规模、文档高分位大小、查询选择性、并发客户端、游标 batch、schema sampling、Explain 执行、导出吞吐和本地历史增长。成本包含客户端升级、签名包分发、堡垒机与代理、Atlas 网络出口、审计日志、样本加密存储、权限评审和恢复演练。一个“免费 GUI”若让每个人都导出生产数据,长期成本会远高于许可证。
团队基线应固定受支持的 Compass 与 mongosh 发行线、安装来源和包完整性;连接模板只保存无密码 URI;生产默认 read 或更窄的自定义 role;索引、写入、导入和终止操作使用临时提权;Atlas network access、数据库用户、X.509/OIDC 身份和 SSH key 有统一 owner 与到期回收;季度恢复演练验证样本销毁、凭据撤销和升级兼容。
机制细节可继续查阅 Compass connection options、Compass required access、mongosh options、mongosh settings 与 Explain results。一套可接受的客户端链路必须同时证明:URI 解析到预期拓扑,受限身份只读成功且写入失败,查询计划与容量证据可解释,历史与导出可追踪并能按时销毁,访问撤销后旧入口确实失效。
