424Pay — Database Index Optimization Report

รายงานการเพิ่มประสิทธิภาพการ query ฐานข้อมูล MongoDB
Environment: Develop
Database: shadow_pay_dev
Cluster: MongoDB Atlas
Status: ✅ Completed
Indexes Created
15 / 15
100% Success
Best Improvement
100%
wallet_adjusts (15ms → 0ms)
Scan Reduction
136×
permissions by code
Total Build Time
1.9s
รวมทุก index
Duplicate Issues
0
All Clean

1 Overview สิ่งที่ทำ

ใช้เครื่องมือ cmd/index-toolkit รันบน MongoDB ของ environment develop ทำตาม 5 ขั้นตอน:

  1. Pre-flight Audit — ตรวจ duplicate ในฟิลด์ที่จะสร้าง unique index
  2. Benchmark BEFORE — รัน explain executionStats เก็บ baseline
  3. Create Indexes — สร้าง 15 indexes ทีละตัว
  4. Benchmark AFTER — รัน explain เดิมหลังสร้าง index
  5. Comparison — เปรียบเทียบ stage / time / docs examined
📌 Why Indexes? MongoDB index ทำให้ query ใช้ IXSCAN แทน COLLSCAN — เร็วขึ้น 10-1000 เท่าใน collection ขนาดใหญ่

2 Pre-flight Audit (Duplicate Check)

Collection / Fieldสถานะผล
users.username✓ Passไม่มี username ซ้ำ
users.merchantId✓ Passไม่มี merchantId ซ้ำ
permissions.code✓ Passไม่มี permission code ซ้ำ
payment_providers.code✓ Passไม่มี provider code ซ้ำ
user_roles.{userId, roleId}✓ Passไม่มี role assignment ซ้ำ
✅ Result: Data integrity ของ dev DB อยู่ในสภาพดี — unique indexes ทั้ง 5 ตัวสร้างได้ทันที

3 Indexes ที่สร้าง 15/15 สำเร็จ

#CollectionIndex NameKeysTypeBuild Time
1user_rolesidx_userId{userId:1}Standard132 ms
2user_rolesuniq_userId_roleId{userId:1, roleId:1}Unique86 ms
3usersuniq_username{username:1}Unique Partial89 ms
4usersuniq_merchantId{merchantId:1}Unique Partial94 ms
5usersidx_metaData_memberId{metaData.memberId:1}Standard121 ms
6usersidx_type_isDeleted{type:1, isDeleted:1}Compound130 ms
7permissionsuniq_code{code:1}Unique104 ms
8payment_providersuniq_code{code:1}Unique89 ms
9settlementsidx_member_createdAt{memberId:1, createdAt:-1}Compound75 ms
10settlementsidx_status_createdAt{status:1, createdAt:-1}Compound73 ms
11settlementsidx_settlementRequestId{settlementRequestId:1}Standard76 ms
12payment_providers_memberidx_member_provider_deletedAt{memberId:1, paymentProviderId:1, deletedAt:1}Compound104 ms
13wallet_adjustsidx_member_createdAt{memberId:1, createdAt:-1}Compound76 ms
14transactionsidx_transferType_updatedAt{transferType:1, updatedAt:-1}Compound120 ms
15transaction_logsidx_txnRefId_timestamp{txnRefId:1, timestamp:-1}Compound353 ms

Total Build Time: ~1.9 วินาที

4 ผลเปรียบเทียบ Before vs After

QueryBeforeAfterการปรับปรุง
users by type+isDeleted
(admin/member/staff list)
17ms · scan 288 docs
COLLSCAN
2ms · scan 32 docs
idx_type_isDeleted
88% ↓
wallet_adjusts by memberId 15ms · in-memory SORT
COLLSCAN
0ms · ใช้ index
idx_member_createdAt
100% ↓
permissions by code scan 136 docs
COLLSCAN
scan 1 doc
EXPRESS_IXSCAN
136× ↓
payment_providers by code scan 14 docs
COLLSCAN
scan 1 doc
EXPRESS_IXSCAN
14× ↓
user_roles by userId
(HOT path)
scan 50 docs
COLLSCAN
scan 50 keys
IXSCAN idx_userId
Same time @ dev scale ใน prod เห็นชัดมาก

5 Impact Ranking (เรียงตาม ROI)

TIER 1 Critical Hot Path

user_roles.idx_userId — ทุก authenticated request ผ่าน Permission Middleware

payment_providers.uniq_code — ทุก payment transaction (4 hot paths)

TIER 2 Frequent UI

users.idx_type_isDeleted — admin/member/staff list (3 endpoints) — 88% faster confirmed

TIER 3 Specific Feature

wallet_adjusts.idx_member_createdAt — 100% improvement (กำจัด in-memory SORT)

TIER 4 Data Integrity

permissions.uniq_code — ป้องกัน duplicate ในระบบ permission

6 Limitations & Next Steps

⚠️ สิ่งที่ verify ไม่ได้ใน dev

  • users.uniq_username & uniq_merchantId — benchmark ใช้ dummy value
  • user_roles.idx_userId — data dev น้อย (287 docs) ไม่ scale ให้เห็นชัด

🚀 Next Steps

  1. วิเคราะห์ Production (ดู Tab "Production Analysis")
  2. Re-bench ด้วยค่าจริงสำหรับ verify hot-path indexes
  3. Migrate indexes เป็น migration files ถาวร
  4. Schedule production deployment ตาม runbook
Environment: Production
Database: 424pay_production_db
Cluster: MongoDB Atlas M30
Mode: 🔒 Read-only Analysis
🔒 Production Safety: รายงานนี้ใช้ user 424pay-viewer (read-only) ทำเฉพาะ read operations (collStats, aggregate $group, explain) — ไม่มีการสร้าง/แก้ไข index บน production
Total Documents
960K
ทุก collection รวมกัน
Data Size
738 MB
storage 188 MB (compressed)
Slow Query Found
204ms
transactions report query
(scan 152K docs)
Largest Collection
781K
transaction_logs
(630 MB, no TTL)
Index Size
56 MB
7.6% ของ data

1 Database Overview

MetricValue
Database Name424pay_production_db
Total Collections24
Total Documents960,134
Data Size738 MB
Storage Size (compressed)188 MB (75% compression)
Total Index Size56 MB

2 🚨 Critical Finding: Slow Hot-Path Query

ปัญหาที่พบ: Query สำคัญ transactions by transferType sort updatedAt desc ใช้ 204ms ต่อ request และ scan 152,399 documents (100% ของ collection)

รายละเอียด

Query:        transactions.find({transferType: "DEPOSIT"})
              .sort({updatedAt: -1}).limit(50)

Stage:        SORT (in-memory) ❌
Index used:   - (NONE!)
Time:         204 ms
Docs scanned: 152,399 / 152,399 (100%)

ผลกระทบ:

  • ทุกครั้งที่เปิด Report page ใน backoffice ต้อง scan 152K docs + sort ใน memory
  • Memory pressure: ทำ in-memory sort ของ 50K+ docs ทุก request
  • ยิ่งข้อมูลโต ยิ่งช้า แบบ O(n)

แนวทางแก้: เพิ่ม compound index {transferType: 1, updatedAt: -1} → คาดว่าลดเหลือ ~5ms (40× faster)

3 Collection Sizes (Prod vs Dev)

Collection Docs (Prod) Docs (Dev) Data Size Indexes
transaction_logs 🔥781,162112K630 MB3
transactions 🔥152,39920K94 MB4
logging5,4001881.6 MB2 (TTL ✓)
permissions1361360.04 MB1
users1342850.21 MB1
user_roles1342870.02 MB1
member_balances73830.01 MB2 (unique ✓)
payment_providers_member701750.03 MB1
roles14230.01 MB1
settlements131280.01 MB1
payment_providers6140.03 MB1
wallet_adjusts314<0.01 MB1
💡 Insight: Dev มี user data เยอะกว่า prod เพราะ dev เป็น sandbox ทดสอบ แต่ transactions/transaction_logs ใน prod ใหญ่กว่ามาก — ต่างกัน 7-8 เท่า

4 Field Cardinality (Distribution)

การกระจายของข้อมูล — ใช้ประเมินว่า index จะลด scan ratio ได้แค่ไหน

transactions (152K docs)

FieldDistributionผลของ Index
transferTypeDEPOSIT 144.4K (95%) · WITHDRAW 8.0K (5%) · SETTLEMENT 11WITHDRAW query → scan ลด 95%
stateFAILED 76.5K (50%) · COMPLETE 73.4K (48%) · PENDING 2.5K · CONFLICT 7PENDING query → ลด 60×
providerGroupCodeBADOOPAY 95.4K (63%) · MAXPAY 32.9K · HUSKYPAY 20.0K · DPAY 3.9KHigh cardinality → index ดีมาก
isConflictfalse 85K · null 67.4K · true 37filter true → ลด 4,100×!

transaction_logs (781K docs)

FieldDistribution
actionRESPONSE_TO_MEMBER 152.6K · REQUEST_FROM_MEMBER 152.5K · CALLBACK_FROM_PROVIDER 87.4K
txnTypeDEPOSIT 576.5K (74%) · PAYMENT_CONFIRM_CALLBACK 158K · WITHDRAW 32.3K

users (134 docs)

  • type: STAFF 93 (69%) · MEMBER 29 (22%) · ADMIN 12 (9%)
  • status: ACTIVE 110 (82%) · INACTIVE 24
  • isDeleted: false 123 (92%) · true 11

5 Existing Indexes บน Production

CollectionIndexes ที่มีหมายเหตุ
transactionsidx_member_id, idx_reference_id, idx_txn_id⚠️ ขาด idx_updated_at ที่ dev มี
transaction_logsidx_request_id, idx_txn_ref_idOK
member_balancesunique_member_provider_credentialOK ✓
loggingcreatedAt_1 (TTL 1 ปี)OK ✓
9 collections อื่นมีแค่ _id_ (default)❌ ขาด index ทั้งหมด
⚠️ Notice: Production ขาด indexes ที่ dev มีไว้บางตัว → migrate ไม่ครบทุก env

6 ROI Estimation: Impact ของแต่ละ Index

IndexHot PathCollection SizeEstimated Impact
transactions.{transferType, updatedAt}Report page152K🔥🔥🔥 204ms → ~5ms (40× faster)
transaction_logs.{txnRefId, timestamp:-1}Latest log lookup781K🔥🔥🔥 ลด in-memory sort 781K docs
transactions.{state, updatedAt:-1}Filter by status152K🔥🔥 PENDING query ลด 60×
users.{type, isDeleted}Admin/staff list134🔥 88% faster (collection เล็ก)
users.username uniqueLogin134🔥🔥 100% scan → 1 doc
users.merchantId uniquePayment callback134🔥🔥🔥 ทุก callback (high freq)
user_roles.userIdPermission check134🔥 cache miss
payment_providers.codePayment routing6impact ต่ำ (collection จิ๋ว)
permissions.codeSeed136impact ต่ำ

7 ลำดับความสำคัญในการสร้าง Index บน Production

PRIORITY 1 ผลทันทีและชัดเจน

  1. transactions.{transferType, updatedAt:-1} → แก้ปัญหา report page ช้า 204ms
  2. transaction_logs.{txnRefId, timestamp:-1} → ลด memory pressure จาก in-memory sort 781K docs
  3. transactions.{state, updatedAt:-1} → query by status (PENDING, FAILED, etc.)

PRIORITY 2 Hot Path ที่จะ scale ในอนาคต

  1. users.username unique
  2. users.merchantId unique partial
  3. users.{type, isDeleted}

PRIORITY 3 Data Integrity

  1. permissions.code unique
  2. payment_providers.code unique
  3. user_roles.{userId, roleId} unique compound

PRIORITY 4 Future Scale

  • settlements.{memberId, createdAt}, {status, createdAt}
  • payment_providers_member.{memberId, paymentProviderId, deletedAt}
  • wallet_adjusts.{memberId, createdAt}

8 ⚠️ Critical Concern: transaction_logs ไม่มี TTL

ตัวเลขค่า
Current size781K docs (630 MB)
Time range~7 เดือน (Oct 2025 → May 2026)
Growth rate~110K docs/เดือน
Estimated annual growth1.3M docs/ปี = ~1 GB/ปี
Storage limit (M30)40 GB → ~30-40 ปี (OK)
Index size impactเพิ่มตามขนาด collection → กระทบ RAM working set
คำแนะนำ: logging collection มี TTL 1 ปี แต่ transaction_logs ไม่มี ควรตัดสินใจ retention policy + เพิ่ม TTL หรือ archive ไป cold storage

9 Next Steps สำหรับ Production

  1. วาง maintenance window 02:00-05:00 ICT off-peak
  2. Pre-flight audit บน prod — รัน duplicate check ก่อนสร้าง unique indexes
  3. Backup snapshot ผ่าน Atlas Continuous Cloud Backup
  4. สร้าง indexes ตามลำดับ Priority 1 → 4
  5. Monitor หลัง deploy: CPU < 85%, replica lag < 30s, p95 latency
  6. ตัดสินใจ retention policy ของ transaction_logs
📋 อ้างอิง: ดู docs/INDEX_PRODUCTION_RUNBOOK.md สำหรับ runbook รายละเอียดเฉพาะ Atlas M30