bigywater.tech
MongoDB · 81

profiler 与 slowms

慢查询日志和 system.profile 是两回事吗?slowms 到底影响什么?把 profiler 开到级别 2 会拖慢多少?这篇逐个验证。

28
slowms = 5 时 20 次全表扫描产生的 Slow query 日志条数(20 find + 8 getMore)
1.15×
profile 级别 2 相对级别 0 的耗时
4,280
级别 2 跑 18,000 次操作后 4 MB system.profile 里剩下的条数

默认值

实测 · 本机执行输出
  test> db.getProfilingStatus()
  {
    was: 0,
    slowms: 100,
    slowinprogms: Long('5000'),
    sampleRate: 1,
    ok: 1
  …(共 7 行)
  test> db.system.profile.countDocuments({})
  0

profile 级别 0,slowms = 100,sampleRate = 1;system.profile 是空的。

slowms 与日志

实测 · 本机执行输出
  阈值 100 ms:跑了 20 次全表扫描后,日志里新增的 "Slow query" 数 = 0
  test> db.setProfilingLevel(0, {slowms: 5})
  {
    was: 0,
    slowms: 100,
    slowinprogms: Long('5000'),
  …(共 7 行)
  阈值 5 ms:同样的 20 次之后,新增 "Slow query" = 28  按命令分: {'find': 20, 'getMore': 8}
  最后一条 find 的 Slow query 日志的字段: ['appName', 'collectionType', 'command', 'cursorid', 'docsExamined', 'durationMillis', 'hasSortStage', 'inUseTrackedMemBytes', 'isFromPriorityPortConnection', 'isFromUserConnection', 'keysExamined', 'locks', 'nBatches', 'nreturned', 'ns', 'numInterruptChecks', 'numYields', 'opid', 'peakTrackedMemBytes', 'planCacheKey', 'planCacheShapeHash', 'planRanker', 'planSummary', 'planningTimeMicros', 'priorityLowered', 'protocol', 'queryFramework', 'queryHash', 'queryShapeHash', 'queues', 'remote', 'reslen', 'storage', 'type', 'wasMarkedNonDeprioritizable', 'workingMillis']
  其中 durationMillis = 15,docsExamined = 50000,planSummary = COLLSCAN,keysExamined = 0,nreturned = 101

超过 slowms 的操作写进 mongod 日志(Slow query),profile 级别为 0 时也会写;日志按操作记,find 和 getMore 各一条,字段包括 durationMillis、docsExamined、planSummary。

级别 1 与 2

实测 · 本机执行输出
  test> db.setProfilingLevel(1, {slowms: 5})
  { was: 0, slowms: 5, slowinprogms: Long('5000'), sampleRate: 1, ok: 1 }
  test> db.system.profile.aggregate([{$group: {_id: {op: '$op', ns: '$ns', plan: '$planSummary'}, n: {$sum: 1}, maxMillis: {$max: '$millis'}}}, {$sort: {'_id.op': 1}}]).toArray()
  [
    {
      _id: { op: 'getmore', ns: 'test.t', plan: 'COLLSCAN' },
      n: 5,
      maxMillis: 8
    },
    {
      _id: { op: 'query', ns: 'test.t', plan: 'COLLSCAN' },
      n: 5,
      maxMillis: 18
    }
  ]
  test> db.system.profile.countDocuments({ns: 'test.small'})  // findOne 很快,级别 1 没记录
  0
  test> db.setProfilingLevel(2)
  { was: 1, slowms: 5, slowinprogms: Long('5000'), sampleRate: 1, ok: 1 }
  test> db.small.findOne({_id: 1})
  { _id: 1 }
  test> db.system.profile.find({ns: 'test.small'}).sort({ts: -1}).limit(1)  // 级别 2 下 findOne 的一条记录
  {
    op: 'query',
    ns: 'test.small',
    isFromPriorityPortConnection: false,
    command: {
      find: 'small',
      filter: { _id: 1 },
      limit: 1,
      lsid: { id: UUID('16a05724-c041-4ce3-8974-b910045ccff0') },
      '$db': 'test'
    },
    opid: 83972,
    keysExamined: 1,
    docsExamined: 1,
    nBatches: 1,
    cursorExhausted: true,
    numYield: 0,
    nreturned: 1,
    queryFramework: 'classic',
    planRanker: 'none',
    locks: { Global: { acquireCount: { r: Long('1') } } },
    flowControl: {},
    responseLength: 116,
    protocol: 'op_msg',
    queues: {
      ingress: { admissions: 1, totalTimeQueuedMicros: Long('0') },
      ingress_request: {},
      execution: { admissions: 1, totalTimeQueuedMicros: Long('0') },
      writeThrottle: {}
    },
    millis: 0,
    micros: 73,
    planSummary: 'EXPRESS_IXSCAN { _id: 1 }',
    planningTimeMicros: 31,
    execStats: {
      isCached: false,
      stage: 'EXPRESS_IXSCAN',
      keyPattern: { _id: 1 },
      indexName: '_id_',
      nReturned: 1,
  …(共 50 行)
  test> Object.keys(<那条记录>).join(', ')
  op, ns, isFromPriorityPortConnection, command, opid, keysExamined, docsExamined, nBatches, cursorExhausted, numYield, nreturned, queryFramework, planRanker, locks, flowCo
  test> db.setProfilingLevel(0)
  { was: 2, slowms: 5, slowinprogms: Long('5000'), sampleRate: 1, ok: 1 }

级别 1 把超过 slowms 的操作写进 system.profile,级别 2 写所有操作;每条记录有命令、keysExamined、docsExamined、planSummary、耗时等。

capped

实测 · 本机执行输出
  test> db.system.profile.stats().capped, maxSize
  true 1048576
  test> db.setProfilingLevel(0); db.system.profile.drop(); db.createCollection('system.profile', {capped: true, size: 4 * 1024 * 1024}); db.setProfilingLevel(2); db.system.profile.stats().maxSize
  4194304
  test> db.setProfilingLevel(0)
  { was: 2, slowms: 5, slowinprogms: Long('5000'), sampleRate: 1, ok: 1 }

system.profile 是 1 MB 的 capped 集合;想放大要关 profile、drop、再按大小重建。

开销

实测 · 本机执行输出
  级别 0:293 ms;级别 1:295 ms;级别 2:337 ms(级别 2 是级别 0 的 1.15 倍)
  级别 2 下一共执行了 (1 次预热 + 5 次) × 3,000 = 18,000 次 findOne
  test> db.system.profile.countDocuments({})  // 级别 2 跑完后,system.profile 是 4 MB 的 capped 集合
  4280

级别 2 让 3,000 次 findOne 从 293 ms 变成 337 ms(1.15 倍),并且旧记录会被覆盖:18,000 次操作后只剩 4,280 条。

sampleRate

实测 · 本机执行输出
  test> db.getProfilingStatus()
  {
    was: 0,
    slowms: 0,
    slowinprogms: Long('5000'),
    sampleRate: 0.1,
    ok: 1
  …(共 7 行)
  slowms = 0、sampleRate = 0.1:1,000 次 findOne 后日志里新增 "Slow query" = 104

slowms = 0 加 sampleRate = 0.1,1,000 次操作只记录了 104 条日志。

分步演示

profiler 与 slowms
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / MongoDB 9.0.2(Homebrew 的 mongod、mongos)/ mongosh 2.12.0 / pymongo 4.18.3;每个实验在临时目录里起全新的 mongod(独立 dbpath,端口 9900 以上,只监听 127.0.0.1,WiredTiger 缓存 0.25 GB),需要副本集或分片集群时在本机起多个进程;命令输出来自真实的 mongosh,批量写入、计时与多线程用 pymongo;计时用 time.perf_counter,数字每次运行会有小幅抖动,ObjectId、时间戳等每次运行都不同。单机 mongod,5 万个文档的集合;mongosh 做交互,pymongo 做计时
    • 没有验证:sampleRate 对 system.profile(而不是日志)的影响;slowinprogms(进行中的慢操作日志)的行为;按集合/操作类型过滤的 profile 过滤器(filter 选项);副本集上 secondary 的 profile;日志的 JSON 结构在其它版本的差异;并发压力下 profile 级别 2 的锁争用
    京ICP备2026067502号-1京公网安备11010502063487号