Query Performance Profiler๏ƒ

Overview๏ƒ

Query Performance Profiler ื”ื•ื ื›ืœื™ ื ื™ื˜ื•ืจ ืœืฉืื™ืœืชื•ืช MongoDB ืื™ื˜ื™ื•ืช, ื”ืžืกืคืง:

  1. ื–ื™ื”ื•ื™ ืฉืื™ืœืชื•ืช ืื™ื˜ื™ื•ืช โ€“ ืžืขืงื‘ ื‘ื–ืžืŸ ืืžืช ืื—ืจื™ ืฉืื™ืœืชื•ืช ืฉื—ื•ืจื’ื•ืช ืžืกืฃ ื–ืžืŸ ืžื•ื’ื“ืจ

  2. ื ื™ืชื•ื— Explain Plans โ€“ ื”ืฆื’ื” ื•ื™ื–ื•ืืœื™ืช ืฉืœ ืชื•ื›ื ื™ืช ื”ื‘ื™ืฆื•ืข ืฉืœ MongoDB (ื›ื•ืœืœ Aggregation Pipelines)

  3. ื”ืžืœืฆื•ืช ืื•ืคื˜ื™ืžื™ื–ืฆื™ื” โ€“ ื”ืฆืขื•ืช ืื•ื˜ื•ืžื˜ื™ื•ืช ืœืฉื™ืคื•ืจ ื‘ื™ืฆื•ืขื™ื

  4. ื”ื™ืกื˜ื•ืจื™ื™ืช ืฉืื™ืœืชื•ืช โ€“ ืฉืžื™ืจื” ื•ื ื™ืชื•ื— ืฉืœ ื“ืคื•ืกื™ ืฉืื™ืœืชื•ืช ืœืื•ืจืš ื–ืžืŸ

ืงื”ืœ ื™ืขื“๏ƒ

ื”ืคืจื•ืคื™ื™ืœืจ ืžื™ื•ืขื“ ืœ-Admin ื‘ืœื‘ื“. ื’ื™ืฉื” ืืœื™ื• ื“ื•ืจืฉืช:

  • ื”ืจืฉืืช Admin ื‘-WebApp, ืื•

  • ื˜ื•ืงืŸ ื™ื™ืขื•ื“ื™ (X-Profiler-Token)

ืžื” ื”ื›ืœื™ ืœื ืขื•ืฉื”๏ƒ

  • ืœื ืžื—ืœื™ืฃ ืืช MongoDB Profiler ื”ืžื•ื‘ื ื” ื‘ืจืžืช ื”-DB

  • ืœื ืžืกืคืง ืื•ืคื˜ื™ืžื™ื–ืฆื™ื” ืื•ื˜ื•ืžื˜ื™ืช (ืจืง ื”ืžืœืฆื•ืช)

  • ืœื ืžื™ื•ืขื“ ืœ-Production Debugging ื‘ื–ืžืŸ ืืžืช ืฉืœ ืฉืื™ืœืชื•ืช ื‘ื•ื“ื“ื•ืช

ืžืžืฉืง ืžืฉืชืžืฉ (WebApp)๏ƒ

ื”ื ืชื™ื‘๏ƒ

GET /admin/profiler

ืื™ืš ืœื”ื’ื™ืข๏ƒ

  1. ื“ืจืš Settings โ†’ ื›ืœื™ ืื“ืžื™ืŸ โ†’ Query Profiler

  2. ืื• ื™ืฉื™ืจื•ืช ืœื›ืชื•ื‘ืช /admin/profiler

ืžื” ืจื•ืื™ื ื‘ื“ืฉื‘ื•ืจื“๏ƒ

ืื–ื•ืจ

ืชื™ืื•ืจ

Summary

ืกื™ื›ื•ื ื›ืœืœื™: ืžืกืคืจ ืฉืื™ืœืชื•ืช ืื™ื˜ื™ื•ืช, ื–ืžืŸ ืžืžื•ืฆืข, collections ืžื•ืฉืคืขื™ื

Slow Queries Table

ื˜ื‘ืœื” ืขื ืฉืื™ืœืชื•ืช ืื™ื˜ื™ื•ืช, ื›ื•ืœืœ ืกื™ื ื•ืŸ ืœืคื™ collection

ื ื™ืชื•ื— Query/Pipeline

ื˜ื•ืคืก ืœื”ื–ื ืช ืฉืื™ืœืชื” ืœื ื™ืชื•ื— ืžื™ื™ื“ื™

Explain Visualization

ื•ื™ื–ื•ืืœื™ื–ืฆื™ื” ืฉืœ ืฉืœื‘ื™ ื”ื‘ื™ืฆื•ืข (COLLSCAN, IXSCAN, FETCH ื•ื›ื•โ€™)

Recommendations

ื”ืžืœืฆื•ืช ืื•ืคื˜ื™ืžื™ื–ืฆื™ื” ืœืคื™ ื—ื•ืžืจื” (ืงืจื™ื˜ื™/ืื–ื”ืจื”/ืžื™ื“ืข)

Note

ื‘ืจื™ืจืช ื”ืžื—ื“ืœ ืฉืœ ื”-verbosity ื”ื™ื queryPlanner (ื‘ื˜ื•ื—) โ€“ ืœื ืžืจื™ืฅ ืืช ื”ืฉืื™ืœืชื” ื‘ืคื•ืขืœ.

API Reference๏ƒ

Authentication๏ƒ

ืื PROFILER_AUTH_TOKEN ืžื•ื’ื“ืจ, ื™ืฉ ืœืฉืœื•ื— ืืช ื”ื˜ื•ืงืŸ ื‘ื›ื•ืชืจืช:

X-Profiler-Token: <your-token>

ืื ื”ื˜ื•ืงืŸ ืœื ืžื•ื’ื“ืจ, ื”ื’ื™ืฉื” ืžืชื‘ืกืกืช ืขืœ ื”ืจืฉืืช Admin ื‘-WebApp Session.

Endpoints๏ƒ

Method

Endpoint

ืชื™ืื•ืจ

GET

/api/profiler/summary

ืกื™ื›ื•ื ืžืฆื‘ ื”ืคืจื•ืคื™ื™ืœืจ

GET

/api/profiler/slow-queries

ืจืฉื™ืžืช ืฉืื™ืœืชื•ืช ืื™ื˜ื™ื•ืช (ืขื ืกื™ื ื•ืŸ)

POST

/api/profiler/explain

ื”ืจืฆืช Explain Plan ืขืœ ืฉืื™ืœืชื”/pipeline

POST

/api/profiler/recommendations

ื ื™ืชื•ื— ื•ื”ืžืœืฆื•ืช ืœืฉืื™ืœืชื”

POST

/api/profiler/analyze

Alias ืœ-recommendations

GET

/api/profiler/collection/<name>/stats

ืกื˜ื˜ื™ืกื˜ื™ืงื•ืช collection (ื’ื•ื“ืœ, ืื™ื ื“ืงืกื™ื)

GET /api/profiler/summary๏ƒ

ืžื—ื–ื™ืจ ืกื™ื›ื•ื ื›ืœืœื™:

curl -H "X-Profiler-Token: $TOKEN" \
     https://your-app.com/api/profiler/summary

Response:

{
  "status": "success",
  "data": {
    "total_slow_queries": 42,
    "collections_affected": ["code_snippets", "users"],
    "avg_execution_time_ms": 350.5,
    "max_execution_time_ms": 2500.0,
    "unique_patterns": 15,
    "threshold_ms": 100
  }
}

GET /api/profiler/slow-queries๏ƒ

Query Parameters:

Parameter

ืชื™ืื•ืจ

ื‘ืจื™ืจืช ืžื—ื“ืœ

limit

ืžืกืคืจ ืฉืื™ืœืชื•ืช ืœื”ื—ื–ื™ืจ

50

collection

ืกื™ื ื•ืŸ ืœืคื™ collection

(ื”ื›ืœ)

min_time

ื–ืžืŸ ื‘ื™ืฆื•ืข ืžื™ื ื™ืžืœื™ (ms)

(ื”ื›ืœ)

hours

ืฉืื™ืœืชื•ืช ืžื”ืฉืขื•ืช ื”ืื—ืจื•ื ื•ืช

(ื”ื›ืœ)

ื“ื•ื’ืžื”:

curl -H "X-Profiler-Token: $TOKEN" \
     "https://your-app.com/api/profiler/slow-queries?limit=20&collection=code_snippets&hours=24"

POST /api/profiler/explain๏ƒ

ืžืจื™ืฅ Explain Plan ืขืœ ืฉืื™ืœืชื” ืื• Aggregation Pipeline.

Body (Query):

{
  "collection": "code_snippets",
  "query": {"user_id": "123", "is_deleted": false},
  "verbosity": "queryPlanner"
}

Body (Pipeline):

{
  "collection": "code_snippets",
  "pipeline": [
    {"$match": {"user_id": "123"}},
    {"$group": {"_id": "$language", "count": {"$sum": 1}}}
  ],
  "verbosity": "queryPlanner"
}

ื“ื•ื’ืžื”:

curl -X POST \
     -H "X-Profiler-Token: $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"collection":"code_snippets","query":{"user_id":"<value>"}}' \
     https://your-app.com/api/profiler/explain

POST /api/profiler/recommendations๏ƒ

ืžื—ื–ื™ืจ Explain Plan + ื”ืžืœืฆื•ืช ืื•ืคื˜ื™ืžื™ื–ืฆื™ื”:

curl -X POST \
     -H "X-Profiler-Token: $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"collection":"code_snippets","query":{"user_id":"<value>"}}' \
     https://your-app.com/api/profiler/recommendations

GET /api/profiler/collection/<name>/stats๏ƒ

ืžื—ื–ื™ืจ ืกื˜ื˜ื™ืกื˜ื™ืงื•ืช collection:

curl -H "X-Profiler-Token: $TOKEN" \
     https://your-app.com/api/profiler/collection/code_snippets/stats

Response:

{
  "status": "success",
  "data": {
    "size_bytes": 1048576,
    "count": 5000,
    "avg_obj_size": 210,
    "index_count": 3,
    "indexes": ["_id_", "user_id_1", "user_id_1_is_deleted_1"],
    "total_index_size": 524288
  }
}

Security๏ƒ

Authentication๏ƒ

ืฉื›ื‘ื”

ืžืฉืชื ื”/ืžื ื’ื ื•ืŸ

ืชื™ืื•ืจ

Token

PROFILER_AUTH_TOKEN

ื˜ื•ืงืŸ ื ืฉืœื— ื‘-Header X-Profiler-Token

IP Allowlist

PROFILER_ALLOWED_IPS

ืจืฉื™ืžืช IPs ืžื•ืจืฉื™ื (CSV)

Rate Limit

PROFILER_RATE_LIMIT

ืžื’ื‘ืœืช ื‘ืงืฉื•ืช ืœื“ืงื” (ื‘ืจื™ืจืช ืžื—ื“ืœ: 60)

Admin Session

WebApp

ืื ืื™ืŸ Token, ื ื“ืจืฉืช ื”ืจืฉืืช Admin

ื”ื’ื“ืจืช Token๏ƒ

# .env
PROFILER_AUTH_TOKEN=my-secure-profiler-token
PROFILER_ALLOWED_IPS=127.0.0.1,10.0.0.1

ืื–ื”ืจืช Observer Effect๏ƒ

Warning

Observer Effect โ€“ ื”ืจืฆืช explain("executionStats") ืื• explain("allPlansExecution") ืžืจื™ืฆื” ืืช ื”ืฉืื™ืœืชื” ื‘ืคื•ืขืœ!

ื”ืกื™ื›ื•ื ื™ื:

  • ืื ื”ืฉืื™ืœืชื” ืื™ื˜ื™ืช ื›ื™ ื”ื™ื ืžืขืžื™ืกื” ืขืœ ื”-CPU, ื”ืจืฆืช ื”-Explain ืชื›ืคื™ืœ ืืช ื”ืขื•ืžืก

  • ืื ื”ืฉืื™ืœืชื” ื ื•ืขืœืช ืžืกืžื›ื™ื (write operations), ื–ื” ืขืœื•ืœ ืœื”ื—ืžื™ืจ ืืช ื”ืžืฆื‘

  • ื‘-Production ืขืžื•ืก, ื”ืจืฆื” ืื•ื˜ื•ืžื˜ื™ืช ืฉืœ explain ื™ื›ื•ืœื” ืœื™ืฆื•ืจ โ€œืืคืงื˜ ืฉืœื’โ€

ื”ืžืœืฆื•ืช:

  1. ื”ืฉืชืžืฉ ื‘-``queryPlanner`` ื›ื‘ืจื™ืจืช ืžื—ื“ืœ โ€“ ืœื ืžืจื™ืฅ ืืช ื”ืฉืื™ืœืชื”, ืจืง ืžืฆื™ื’ ืืช ื”ืชื•ื›ื ื™ืช

  2. ื”ืจืฅ ``executionStats`` ืจืง ืœืคื™ ื“ืจื™ืฉื” โ€“ ื›ืคื™ ืฉืžืžื•ืžืฉ ื‘ื›ืคืชื•ืจ โ€œื ืชื—โ€ ื‘ื“ืฉื‘ื•ืจื“

  3. ืืœ ืชืจื™ืฅ explain ืื•ื˜ื•ืžื˜ื™ืช ืœื›ืœ ืฉืื™ืœืชื” ืื™ื˜ื™ืช โ€“ ื–ื” ื™ื›ืคื™ืœ ืืช ื”ื‘ืขื™ื”

  4. ืฉืงื•ืœ ื”ืจืฆืช explain ื‘ืฉืขื•ืช ืฉืคืœ ืื• ืขืœ replica secondary

ืจืžื•ืช Verbosity๏ƒ

ืจืžื”

ืชื™ืื•ืจ

ืžืชื™ ืœื”ืฉืชืžืฉ

queryPlanner

ืชื•ื›ื ื™ืช ื‘ืœื‘ื“, ืœืœื ื”ืจืฆื”

ืœื‘ื“ื™ืงืช ืื™ื ื“ืงืกื™ื (ื‘ื˜ื•ื—)

executionStats

ื›ื•ืœืœ ืกื˜ื˜ื™ืกื˜ื™ืงื•ืช ื‘ื™ืฆื•ืข

ื ื™ืชื•ื— ื‘ื™ืฆื•ืขื™ื ืžืœื

allPlansExecution

ื›ืœ ื”ืชื•ื›ื ื™ื•ืช ืฉื ื‘ื—ื ื•

Debug ืžืชืงื“ื ื‘ืœื‘ื“

Privacy / PII๏ƒ

Important

ื ืจืžื•ืœ ืฉืื™ืœืชื•ืช ืžื•ื ืข ื“ืœื™ืคืช ืžื™ื“ืข ืื™ืฉื™ (PII)

ื”ืคื•ื ืงืฆื™ื” _normalize_query_shape ืžื—ืœื™ืคื” ืืช ื›ืœ ื”ืขืจื›ื™ื ื‘ืคืœื™ื™ืกื”ื•ืœื“ืจื™ื:

  • ืขืจื›ื™ื ืคืฉื•ื˜ื™ื โ†’ <value>

  • ืžืขืจื›ื™ื โ†’ <N items>

  • null โ†’ <null>

ื“ื•ื’ืžื”:

# Query ืžืงื•ืจื™ (ืœื ืžื•ืฆื’)
{"email": "john@example.com", "status": {"$in": ["active", "pending"]}}

# Query ืžื ื•ืจืžืœ (ืžื” ืฉืžื•ืฆื’ ื‘ื“ืฉื‘ื•ืจื“)
{"email": "<value>", "status": {"$in": ["<2 items>"]}}

Warning

ืืœ ืชืชืขื“ ืื• ืชืฆื™ื’ ื“ื•ื’ืžืื•ืช ืขื ื ืชื•ื ื™ ืืžืช/PII ื‘ื“ื•ื—ื•ืช ืื• ื‘ืœื•ื’ื™ื.

Persistence๏ƒ

Collection๏ƒ

ืฉื: slow_queries_log

TTL Index๏ƒ

ืžื—ื™ืงื” ืื•ื˜ื•ืžื˜ื™ืช ืื—ืจื™ 7 ื™ืžื™ื:

db.slow_queries_log.createIndex(
  {"timestamp": 1},
  {expireAfterSeconds: 604800, name: "ttl_cleanup"}
)

ืื™ื ื“ืงืกื™ื ื ื•ืกืคื™ื๏ƒ

// ื—ื™ืคื•ืฉ ืžื”ื™ืจ ืœืคื™ collection + ื–ืžืŸ
db.slow_queries_log.createIndex(
  {"collection": 1, "timestamp": -1},
  {name: "collection_timestamp"}
)

// ื—ื™ืคื•ืฉ ืœืคื™ ื“ืคื•ืก ืฉืื™ืœืชื”
db.slow_queries_log.createIndex(
  {"query_id": 1},
  {name: "query_pattern"}
)

Metrics (Prometheus)๏ƒ

ืžื˜ืจื™ืงื•ืช ื–ืžื™ื ื•ืช ื›ืืฉืจ PROFILER_METRICS_ENABLED=true:

Metric

Type

ืชื™ืื•ืจ

mongodb_slow_queries_total

Counter

ืžืกืคืจ ืฉืื™ืœืชื•ืช ืื™ื˜ื™ื•ืช ืœืคื™ collection ื•-operation

mongodb_query_duration_seconds

Histogram

ื”ืชืคืœื’ื•ืช ื–ืžื ื™ ืฉืื™ืœืชื•ืช

mongodb_collscan_detected_total

Counter

ืžืกืคืจ COLLSCAN ืฉื–ื•ื”ื•

query_profiler_buffer_size

Gauge

ืžืกืคืจ ืฉืื™ืœืชื•ืช ื‘ื‘ืืคืจ ื”ื–ื™ื›ืจื•ืŸ

Environment Variables๏ƒ

ืจืื• ืืช ื”ื˜ื‘ืœื” ื”ืžืœืื” ื‘-ืžืฉืชื ื™ ืกื‘ื™ื‘ื” - ืจืคืจื ืก.

ืžืฉืชื ื”

ืชื™ืื•ืจ

ื‘ืจื™ืจืช ืžื—ื“ืœ

PROFILER_ENABLED

ื”ืคืขืœืช Query Performance Profiler

true

PROFILER_SLOW_THRESHOLD_MS

ืกืฃ ื–ืžืŸ (ms) ืœื”ื’ื“ืจืช โ€œืฉืื™ืœืชื” ืื™ื˜ื™ืชโ€

100

PROFILER_MAX_BUFFER_SIZE

ืžืกืคืจ ืฉืื™ืœืชื•ืช ื‘ื–ื™ื›ืจื•ืŸ

1000

PROFILER_AUTH_TOKEN

ื˜ื•ืงืŸ ื’ื™ืฉื” (Header X-Profiler-Token)

(ืจื™ืง)

PROFILER_ALLOWED_IPS

Allowlist ืฉืœ IPs (CSV)

(ืจื™ืง)

PROFILER_RATE_LIMIT

ืžื’ื‘ืœืช ื‘ืงืฉื•ืช ืœื“ืงื”

60

PROFILER_METRICS_ENABLED

ื”ืคืขืœืช ืžื˜ืจื™ืงื•ืช Prometheus

true

ื”ืžืœืฆื•ืช ืื•ืคื˜ื™ืžื™ื–ืฆื™ื” ื ืคื•ืฆื•ืช๏ƒ

ื‘ืขื™ื”

ืกื™ืžืคื˜ื•ื

ื”ืžืœืฆื”

๐Ÿ”ด COLLSCAN

stage: "COLLSCAN"

ืฆื•ืจ ืื™ื ื“ืงืก ืขืœ ืฉื“ื•ืช ื”ืกื™ื ื•ืŸ

๐ŸŸ  Sort ื‘ื–ื™ื›ืจื•ืŸ

stage: "SORT"

ื”ื•ืกืฃ ืฉื“ื” ืžื™ื•ืŸ ืœืื™ื ื“ืงืก

๐ŸŸก ื™ื—ืก ื™ืขื™ืœื•ืช ื ืžื•ืš

docsExamined >> nReturned

ืฉืคืจ selectivity ืฉืœ ื”ืื™ื ื“ืงืก

๐Ÿ”ด $lookup ืœืœื ืื™ื ื“ืงืก

nestedLoopJoin

ืฆื•ืจ ืื™ื ื“ืงืก ืขืœ ื”-foreign field

๐ŸŸ  $sort ืžืฉืชืžืฉ ื‘ื“ื™ืกืง

usedDisk: true

ื”ื•ืกืฃ $match ืœืคื ื™ ื”-$sort

ืงื™ืฉื•ืจื™ื ื ื•ืกืคื™ื๏ƒ