简介
一份分步指南,帮助你打开、使用并验证新版 Profiler 界面。请按顺序从 Step 1 走到结尾。每个步骤都会告诉你该做什么以及会看到什么。你不需要预先了解任何 profiling 知识 — 💡 提示框会在概念出现时立即解释。
如何阅读本手册
每个步骤包含:
- 操作 — 准确的动作(点击、输入、运行某段脚本)。
- 查看 — 屏幕上应该发生什么。这就是你的“通过 / 失败”。
- 原因 — 仅在有助于理解时出现(如果你赶时间可以跳过)。
概念
概念框。 在某个术语出现时立刻解释它。
注意
注意。 一个容易让人困惑的细节。
开始之前
概念
Profiler 是什么
MongoDB 可以保留一本它运行过的所有内容的“日志” — 每条查询、用时多久、是否使用了索引 — 存放在一个名为 system.profile 的集合中。Profiler 界面会打开这本日志并以图形化方式展示:图表、过滤器,甚至是索引建议。
你需要:
- 1在最后一次构建之后重启 dev 服务器(否则界面甚至无法加载 — 这正是引起
spacingPx错误的原因)。 - 2一个已连接的 MongoDB 连接。可以是本地、自建,或 Atlas M10+。Atlas M0/M2/M5(免费)上 profiler 被 MongoDB 屏蔽 — 它可以工作,但界面会提示其受限(我们在 Step 41 测试这一点)。
- 3数据库用户需要能够运行管理类命令(
setProfilingLevel、createIndex)。一个“database owner”用户即可胜任。
完整走读预计时间:约 30 分钟。
准备实验数据库
为了让 Profiler 有内容可显示,我们先创建一个包含大量数据的数据库。
创建 profiler_lab 数据库
在 NoSqlStudio 中打开 Mongo Shell 标签(或 Scratchpad),粘贴下面的脚本并运行。
use profiler_lab;
db.dropDatabase();
use profiler_lab;
['orders', 'products', 'customers'].forEach((c) => db.getCollection(c).drop());
function seed(coll, n, gen) {
const c = db.getCollection(coll);
let buf = [];
for (let i = 0; i < n; i++) {
buf.push(gen(i));
if (buf.length === 165000) { c.insertMany(buf); buf = []; }
}
if (buf.length) c.insertMany(buf);
print(coll + ': ' + c.countDocuments());
}
const ST = ['pending', 'paid', 'shipped', 'cancelled', 'refunded'];
const RG = ['north', 'south', 'east', 'west'];
seed('orders', 120000, (i) => ({
orderNo: i, status: ST[i % 5], region: RG[i % 4],
customerId: (i * 7) % 5000, total: Math.round(Math.random() * 1e5) / 100,
items: 1 + (i % 8), createdAt: new Date(Date.now() - (i % 90) * 864e5),
note: 'order line '.repeat(4),
}));
seed('products', 20000, (i) => ({
sku: 'SKU-' + i, category: ['a', 'b', 'c', 'd', 'e'][i % 5],
price: Math.round(Math.random() * 5e4) / 100, stock: i % 500,
active: i % 3 !== 0,
}));
seed('customers', 5000, (i) => ({
customerId: i, tier: ['free', 'pro', 'enterprise'][i % 3],
city: RG[i % 4], spend: Math.round(Math.random() * 1e6) / 100,
}));几秒钟后,shell 打印:
orders: 120000
products: 20000
customers: 500012 万条 orders 足以让一条没有索引的查询变得明显缓慢 — 而缓慢正是我们想要诊断的对象。
注意
如果之后查询没有超过 100 ms(机器较快),请回到这里把 120000 改成 300000。
打开 Profiler 界面
首次打开
通过以下 3 种方式中的任一方式打开 Profiler(它们都通向同一个地方 — 另外两种留到 Step 46 再测试):
- 主菜单 → Database Profiler,或快捷键 Ctrl+Alt+Shift+P。
- 工具栏 → Monitoring → Database Profiler 菜单。
- 在侧边栏的连接上右键 → Profiler。
一个名为 Profiler 的新标签打开,旁边显示连接名。从上到下,该标签包含:标题栏、控制条,以及四个分区按钮(Dashboard、Live Feed、Index Advisor、Sessions)。
选择数据库
在标题栏的 Database 字段中,选择 `profiler_lab`。
界面现在作用于该数据库。由于我们尚未打开 profiling,Dashboard 显示类似“No profiling data yet”的提示。
注意
profiler 是按数据库划分的。本手册中的所有操作都使用 profiler_lab。如果你选了另一个数据库,就看不到这些触发的结果。
打开 profiling
了解控制条
标题栏正下方是一条控制条,包含:
- Profiling — 三个按钮:
Off·Slow ops·All ops。 - slowms 和 Sample — 两个数字字段 + 一个 Apply 按钮。
- Filter — 打开一个过滤器构建器。
- ⏱ Capture — 安全的定时捕获。
- system.profile — 该集合的小型使用条 + 一个 Resize 按钮。
- Health — 带分数的等级(A–F)。
概念
三个级别。 Off = 不记录任何内容。Slow ops = 只记录超过 slowms 阈值的内容。All ops = 记录所有内容。为了测试,我们选用 All ops,以便看到全部流量。
在“All ops”级别打开
点击 All ops 按钮。
- 左侧的状态点变为蓝色。
- 出现一个警告药丸 “⚠ All ops”(提醒级别 2 会带来性能开销)。
- 一条快速提示确认“Profiling level set to 2”。
这是这个工具的核心。在旧版 profiler 中,这只是为你复制一段命令让你粘贴到 shell。现在点击会真正打开 profiling。如果状态点变蓝,主要的技术链路就工作正常了。✅
产生流量(“触发器”)
现在我们在 shell 中运行一些查询,让 Profiler 有东西可展示。保持 Profiler 标签打开 — 它每几秒会自动刷新。
触发器 A — 慢查询
在 shell 中运行:
use profiler_lab;
db.orders.find({ status: 'shipped' }).toArray().length;
db.orders.find({ region: 'south' }).sort({ total: -1 }).toArray().length;1–3 秒内,在 Live Feed 分区中,出现两条针对 profiler_lab.orders 的新 query 操作行,带有红色的 COLLSCAN 徽标。
概念
COLLSCAN 与 IXSCAN。 COLLSCAN = MongoDB 逐个文档读取,因为没有索引 — 慢。IXSCAN = 使用了索引(一条捷径) — 快。红色徽标 = 不好;蓝色徽标 = 好。
触发器 B — 低效查询
在 shell 中运行:
use profiler_lab;
db.orders.find({ status: 'refunded', region: 'north', items: 7 }).toArray();feed 中再多一行。Examined → Returned 列中的数字变成红色 — 为了返回区区几条它检查了 12 万条文档。
概念
低效。 当数据库“检查”的内容远多于“返回”的内容时,它在做无用功 — 这是一个建立索引的强力候选。
触发器 C — 写操作
在 shell 中运行:
use profiler_lab;
db.orders.insertOne({ orderNo: 999001, status: 'paid', region: 'east', items: 2 });
db.orders.updateMany({ status: 'pending' }, { $set: { flagged: true } });
db.orders.deleteMany({ flagged: true, items: 3 });三条新操作:insert、update、remove。
触发器 D、E、F — aggregation、count 和 cursors
在 shell 中运行:
use profiler_lab;
db.orders.aggregate([
{ $match: { status: 'paid' } },
{ $group: { _id: '$region', n: { $sum: 1 }, rev: { $sum: '$total' } } },
]).toArray();
db.orders.countDocuments({ region: 'west' });
db.orders.distinct('status');
let cur = db.orders.find({}).batchSize(500).limit(5000);
let k = 0; while (cur.hasNext()) { cur.next(); k++; } print(k);command 类型的操作(aggregation、count)以及若干个 getmore(游标按批读取)。
触发器 G — 同一查询重复执行
在 shell 中运行:
use profiler_lab;
for (let i = 0; i < 40; i++) db.orders.find({ status: 'paid' }).limit(10).toArray();feed 里此刻没什么特别的 — 但请记住:在 Step 24,这会在“Query shapes”面板中变成单独一行,计数为 40。
探索 Live Feed
打开 feed
点击 Live Feed 分区(在顶部)。
一张包含操作的表格,最新的在最上面。列:时间、类型、集合、时长(按颜色编码 — 绿色快、红色慢)、plan(COLLSCAN/IXSCAN 徽标)、examined→returned,以及 app。顶部显示“X of Y operations”。
看实时模式工作
再次运行 触发器 A(来自 Step 6),并留意 feed:
use profiler_lab;
db.orders.find({ status: 'shipped' }).toArray().length;
db.orders.find({ region: 'south' }).sort({ total: -1 }).toArray().length;几秒内,新行会自行出现在顶部。
暂停与恢复
在标题栏点击 ● Live 按钮(它会变为 ⏸ Paused)。再次运行触发器 A:
use profiler_lab;
db.orders.find({ status: 'shipped' }).toArray().length;
db.orders.find({ region: 'south' }).sort({ total: -1 }).toArray().length;feed 不会增长 — 它被冻结了。点击 ⏸ Paused 回到 ● Live:它又开始增长。
暂停在你想检查某一行而不希望列表跳动时很有用。
快速过滤(预设芯片)
逐个点击 feed 顶部的芯片:
- Slow > 100ms — 只显示慢操作。
- COLLSCANs — 只显示没有索引的操作(红色徽标)。
- Inefficient — 高亮 Step 7 中的那一条。
- Writes — 只显示 insert/update/remove(来自 Step 8 的)。
- Last 5 min — 只显示最近的流量。
表格收缩为只显示匹配项。激活的芯片保持高亮。再次点击它即可关闭。
细粒度过滤
按顺序执行:
- 1点击 type 芯片(
query、insert、getmore……)以开关它们。 - 2在 All collections 下拉中选择
orders。 - 3在 search 框中输入
refunded。
每个过滤器都会缩小列表。它们相互组合生效。
全部清除
点击 Clear filters(当存在活动过滤器时才会出现)。
表格恢复显示全部内容。
探索 Dashboard
打开 Dashboard
点击 Dashboard 分区。
顶部四张数字卡片(Operations、Slow ops、Collection scans、Average duration),下方是几张图表。
指标(KPI)
四个数字反映你触发过的一切。“Collection scans”显示 COLLSCAN 的百分比 — 越高越糟。
时间线
Operations over time 图按类型(reads、inserts、updates、deletes、commands)堆叠显示带状区域,并配有颜色编码的图例。
索引使用(环形图)
Index usage 环形图把操作分为 COLLSCAN(红)、IXSCAN(蓝)和其他。此刻应该以红色为主导。
延迟分布
按时间区间(0–1ms、……、>5s)分组操作的柱状图。
最热集合(treemap)
方块大小与每个集合上花费的总时间成正比。orders 应该是最大的方块。
点击某个 treemap 方块
点击 orders 方块。
界面跳到 Live Feed,并已经按 orders 进行过滤。(之后回到 Dashboard。)
查询形态
Query shapes 表把具有相同“形态”的查询分组到一起。你在 Step 10 重复 40 次的查询会以单独一行,计数为 40的形式出现,并把总时间汇总。
这就是发现“哪条查询把所有执行加起来对数据库压力最大”的方式 — 即便每次单独执行都很快。
最慢的操作
最后一张表列出 8 条最慢的操作。请记住它,下一步会用到。
钻取一条操作(drill-down)
打开详情面板
点击任意一行 — 可以在 Live Feed、查询形态表,或最慢操作表中。
从右侧滑入一个面板,显示该操作的完整详情。
读懂这些数字
一个网格,包含 Duration、Plan、Yields、Docs examined、Keys examined、Returned。如果该操作低效,会有一条红色警告说它是“strong index candidate”。
请求实时“explain”
点击 Explain this query。
工具会即时运行 explain(),并显示真实的执行计划、执行时间和检查了多少文档 — 这有助于在当前数据库上确认诊断。
概念
Explain。 这是 MongoDB 在说明它打算如何运行这条查询。它能确认查询是否会使用索引。
查看命令与细节
在面板中查看 Command 区域(该操作的 JSON),并点击复制图标。接着点击 Execution stats 与 Raw document 来展开/折叠。
命令被复制到剪贴板;两个区域可以打开或关闭。
关闭面板
用面板顶部的 X 关闭它 — 然后再打开一个,通过点击面板旁的变暗区域来关闭。
两种方式都能让面板消失。
使用 Index Advisor(亮点功能)
生成建议
在 shell 中运行(3 条有问题的查询,重复 30 次):
use profiler_lab;
for (let i = 0; i < 30; i++) {
db.orders.find({ status: 'shipped' }).toArray();
db.orders.find({ region: 'north' }).sort({ createdAt: -1 }).limit(20).toArray();
db.products.find({ category: 'c', active: true }).toArray();
}等待几秒。(下一步继续。)
打开 Index Advisor
点击 Index Advisor 分区。
顶部是一张带等级(A–F)的 Health 卡片 — 多半较低(C/D),因为你产生了大量 COLLSCAN。下方是 index recommendations 列表 — 大约 3 张卡片。
读懂一条建议
每张卡片包含:严重性药丸(High/Medium/Low)、集合、查询“形态”、带有建议索引字段的芯片(附 ↑/↓ 箭头),以及一行影响信息(“N operations · X s total · M scans”)。
概念
为什么是这些字段、这个顺序。 advisor 遵循 ESR 规则:先 equality 字段,再 sort 字段,最后 range 字段。这是让索引最高效的顺序。
一键创建索引
在某张卡片上点击 Create index。在弹窗中检查命令并确认。
出现“Index created”提示。该建议的卡片变为 ✓ Already indexed。
把其余的也建好
对其他建议重复 Step 34。
它们全都变为 ✓ Already indexed。
确认确实变好
再次运行相同的查询
再次运行 Step 31 中的同一段脚本(这里再贴一遍,你不用回翻):
use profiler_lab;
for (let i = 0; i < 30; i++) {
db.orders.find({ status: 'shipped' }).toArray();
db.orders.find({ region: 'north' }).sort({ createdAt: -1 }).limit(20).toArray();
db.products.find({ category: 'c', active: true }).toArray();
}查询再次运行 — 这次你在阶段 8 创建的索引已经生效。
看 COLLSCAN 变成 IXSCAN
切到 Live Feed。
相同的查询现在显示蓝色 IXSCAN 徽标,时长短得多。它们之前读取了 12 万条文档;现在使用了索引。
看 Health 等级上升
回到 Index Advisor(或点击控制条中的 Health 药丸)。
等级上升了(例如从 C/D 到 A/B),建议数量减少。你刚刚完成了一个完整的循环:诊断 → 修复 → 验证。✅
Sessions 与报告
概念
Session。 当前 profiling 状态的一个“快照”,以一个名字保存。它用于比较某次变更的“之前”和“之后”。
保存一个会话
进入 Sessions,输入一个名字(例如 after the indexes),点击 Save session。
该会话出现在列表中,带有它的指标和日期。
对比
在列表中勾选两个条目 — 例如保存的会话与 Current window(它始终在最上方)。
出现一张 Comparison 表,带有按颜色编码的 Δ 列 — 变好时为绿色(COLLSCAN 更少、用时更短),变差时为红色。
导出报告
点击 Export report。
下载一个 .json 文件,包含 health、建议、查询形态以及最慢的操作 — 可以附到工单里或归档备查。
控制与安全功能
安全的定时捕获
概念
为什么有这个。 把级别 2 一直开着并忘掉它在生产中很危险。“capture”会打开级别 2、运行一段倒计时,在结束时自动关闭。
在控制条点击 ⏱ Capture 并选择 30s。在这 30 秒内,运行任意触发器 — 例如触发器 A:
use profiler_lab;
db.orders.find({ status: 'shipped' }).toArray().length;
db.orders.find({ region: 'south' }).sort({ total: -1 }).toArray().length;profiling 切换到级别 2,出现一个带倒计时的药丸。倒计时归零时,级别自动恢复到之前的状态,并提示“Capture complete”。(你也可以点击 Cancel 提前结束。)
Profiling 过滤器
点击 Filter,把 Namespace 填为 orders,点击 Apply filter。然后运行触发器 A:
use profiler_lab;
db.orders.find({ status: 'shipped' }).toArray().length;
db.orders.find({ region: 'south' }).sort({ total: -1 }).toArray().length;之后点击 Filter → Clear filter。
在过滤器生效时,只有 orders 上的操作会被记录(按钮显示“Filter active”)。清除后,恢复为记录全部内容。
管理 system.profile 集合
概念
Capped collection。 profiler 的日志大小固定;装满时会删除最旧的条目。默认(1 MB)会很快被填满。
观察控制条上的 system.profile 条(显示 used / total)。点击 Resize,选择 10 MB,确认。
条目现在显示 ~/10 MB。弹窗提醒你重新调整大小会清掉当前历史。
调整 slowms 和采样率
把 slowms 改成 50,点击 Apply。然后点击 Slow ops 并运行触发器 A:
use profiler_lab;
db.orders.find({ status: 'shipped' }).toArray().length;
db.orders.find({ region: 'south' }).sort({ total: -1 }).toArray().length;在“Slow ops”级别下,只有慢于设定的 slowms 的操作才会进入 feed — 快操作会被忽略。
收尾
其他打开方式
测试 Step 2 中你还没使用过的另外两个入口。
它们都会打开 Profiler 标签。如果某个标签已经打开,就会聚焦到已有的那个,而不是再开一个。
语言
在 Settings → Language 中切换语言(共 5 种)。回到 Profiler。
整个 Profiler 界面都呈现已翻译的状态 — 不会出现 profiler.xyz 这种“原始”字符串。
主题
在 Light、Dark 与 Neon 之间切换主题。
颜色、卡片和图表都会自适应;没有看不见的文字。
清理(完成后)
在 shell 中运行:
use profiler_lab;
db.setProfilingLevel(0);
db.dropDatabase();Profiling 被关闭,实验数据库被清除。
你已验证内容的总结
| 阶段 | 功能 |
|---|---|
| 2–3 | 打开 Profiler(3 种入口)+ 选择数据库 |
| 3 | 真正打开 profiling(Off / Slow / All) |
| 4–5 | Live Feed:实时、暂停、预设、过滤、搜索 |
| 6 | Dashboard:KPI、时间线、环形图、延迟、treemap、查询形态 |
| 7 | Drill-down:详情、实时 explain()、命令、raw |
| 8–9 | Index Advisor:建议、创建索引、验证改进 |
| 10 | Sessions:保存、对比、导出报告 |
| 11 | 定时捕获、过滤、调整大小、slowms |
| 12 | 其他入口、5 种语言、3 种主题 |