บทที่ 2 · Part 1 — Foundation

Numbers & Estimation

Latency/capacity/size anchor ที่ต้องจำ และ back-of-envelope math ผ่านเคส e-wallet ไทย

มีคำถามที่ตอบไม่ได้ถ้าไม่มีตัวเลขในหัว: "ระบบนี้ต้องใช้ฐานข้อมูลกี่ตัว" คนที่ตอบได้ใน 3 นาทีไม่ได้เก่งกว่า — เขาแค่จำ order of magnitude ของ latency กับ capacity ไว้ และคูณเลขในหัวได้ บทนี้คือชุดตัวเลขนั้น

จบบทนี้คุณจะ

  • จำ latency, capacity และ size anchor ระดับ order of magnitude ได้
  • ทำ back-of-envelope estimation ได้ครบ 4 มิติ: QPS, storage, bandwidth, memory
  • รู้ว่า peak factor ที่คนมักคำนวณผิดมาจากไหน
  • ใช้ตัวเลขเพื่อ push back requirement ที่ไม่จริงได้

[!WARNING] ตัวเลขในบทนี้เป็น illustrative ทั้งหมดมีไว้เพื่อสอน รูปร่างของการคำนวณ ไม่ใช่ผลวัดจากระบบของคุณ ก่อนใช้ในการตัดสินใจจริงต้องวัดเอง หรืออ้างจาก vendor benchmark / load test / dashboard ของคุณ

Latency — จากลิสต์คลาสสิกของ Jeff Dean

เป้าหมายคือจำ order of magnitude ไม่ใช่ทศนิยม

Operationเวลาโดยประมาณความหมายกับงานของคุณ
L1 cache reference~1 nsฟรี
Main memory reference~100 nsin-process cache เร็วกว่า network cache ~1,000 เท่า
Compress 1 KB ด้วย codec เร็วๆ~2 µsCPU ถูกกว่า network — compress payload เถอะ
Read 1 MB sequentially จาก memory~50–100 µsserialize object ใหญ่ไม่ฟรี
Round trip ใน AZ เดียวกัน~0.5 msservice ที่เรียกภายใน 50 ครั้ง เสียเวลา network เปล่าๆ 25 ms
SSD random read~100 µs (0.1 ms)index lookup ที่ลง disk รับได้ table scan ไม่รับ
Read 1 MB sequentially จาก SSD~0.5–1 mssequential ชนะ random ขาด — นี่คือเหตุผลที่ LSM-tree มีอยู่
HDD seek~5–10 msถ้ายังมีจานหมุนใน latency path นั่นคือปัญหาของคุณ
Round trip กรุงเทพ ↔ สิงคโปร์~25–35 mscross-region synchronous call ทำลาย p99
Round trip กรุงเทพ ↔ US East~230–250 mshop เดียวก็เกินงบ UX ของ mobile แล้ว
Mobile 4G first-byte บน warm connection~50–150 msTLS handshake บน cold connection บวกอีก 1–2 round trip

วิธีใช้ตารางนี้จริง

เวลาออกแบบ endpoint ให้ไล่ว่ามันมีกี่ round trip แล้วคูณ 0.5 ms ถ้า endpoint หนึ่งเรียก 8 service แบบ sequential คุณจ่าย network ไปแล้ว 4 ms ก่อนคิดงาน CPU หรือ query ใดๆ และตัวเลขนี้จะบวมขึ้นมากที่ p99

ทำไม p99 ไม่เหมือน average เลย

ถ้า service หนึ่งมี p99 = 100 ms และคุณเรียกมันแบบ parallel 10 ตัวแล้วรอทุกตัว โอกาสที่จะ ไม่ เจอตัวช้าเลยคือ 0.99^10 ≈ 90% — แปลว่า 10% ของ request จะช้าอย่างน้อย 100 ms สิ่งที่เคยเป็น "1 ใน 100" กลายเป็น "1 ใน 10"

กำลังวาด chart…

นี่คือหัวใจของ paper The Tail at Scale — fan-out ทำให้ tail latency กลายเป็น latency ปกติ วิธีแก้อยู่ในบทที่ 12 (hedged request, tied request)

Capacity anchors

ตัวเลขวางแผนแบบ conservative สำหรับ VM/container บน cloud สมัยนี้

Componentตัวเลขวางแผนต่อ nodeอะไรทำให้พัง
Stateless API service (4 vCPU)1,000–5,000 req/s สำหรับงาน JSON เบาๆJSON ขนาด 1 MB+, synchronous fan-out, N+1 query, blocking I/O ที่ thread ไม่พอ
Relational DB (managed, 8–16 vCPU)point read 5,000–20,000/s; write 1,000–5,000/swrite ถูกจำกัดด้วย WAL/fsync และจำนวน index — ทุก index ที่เพิ่มเก็บภาษีจากทุก write
Redis (single node)50,000–150,000 ops/s, p99 ต่ำกว่า 1 mskey ใหญ่, KEYS *, คำสั่ง blocking บน single thread, hot key กระจุกที่ shard เดียว
Kafka (cluster เล็ก)หลายร้อย MB/s รวม; ~10k–100k msg/s ต่อ partitionจำนวน partition คือเพดาน parallelism ของ consumer — วางแผนตั้งแต่แรก เพราะเปลี่ยนแล้ว key จะย้าย partition
Object storage (S3-class)throughput เหลือเฟือ; first byte ~10–100 msไม่ใช่ฐานข้อมูล ไม่มี transaction ข้าม object และ LIST ราคาแพง/ช้าเมื่อ prefix มี object จำนวนมาก — ระดับ consistency ต่างกันตาม provider จึงต้องอ่านเอกสารของตัวที่ใช้จริง (Amazon S3 ให้ strong read-after-write สำหรับ PUT/DELETE/GET/LIST แล้ว)
CDN edgeดูดซับ static traffic ได้เกือบทั้งหมดเฉพาะเมื่อ cache key ถูก — Vary header หรือ cookie ตัวเดียวทำให้ hit rate เหลือใกล้ศูนย์

ตัวเลขพวกนี้คือจุดเริ่มของบทสนทนา ไม่ใช่จุดจบ

เอาไปใช้ตอน sizing รอบแรกเพื่อรู้ว่า "ต้องใช้ 3 node หรือ 300 node" แล้วต้องมี load test ยืนยันก่อนขึ้น production เสมอ

รูปแบบการบันทึกตัวเลขที่วัดเอง

ตัวเลขที่ไม่มีบริบทเทียบกันไม่ได้และเชื่อไม่ได้ ทุกครั้งที่วัด ให้บันทึกครบ 7 ช่องนี้ แล้วเก็บไว้ในที่เดียวของทีม

Component        Postgres 16, db.r7g.2xlarge (8 vCPU / 64 GB), gp3 12k IOPS
Workload         INSERT ledger_entries 5 แถว/txn, 6 index บนตาราง
Payload          ~400 bytes/แถว
Concurrency      64 connection (pgbouncer transaction pooling)
Durability       synchronous_commit = on, 1 sync standby ใน AZ อื่น
ผลลัพธ์           p50 4 ms · p95 11 ms · p99 28 ms · 1,850 txn/s ที่จุดอิ่มตัว
วันที่วัด          2026-07-20   ผู้วัด <ชื่อ>

ทำไมช่อง Durability สำคัญที่สุด

ตัวเลข write throughput ที่วัดโดยปิด synchronous_commit หรือไม่มี sync standby สูงกว่าของจริงหลายเท่า ตัวเลขที่ไม่บอก durability configuration คือตัวเลขที่ใช้ตัดสินใจไม่ได้

Size anchors

  • 1 UUID = 16 bytes แบบ binary, 36 chars แบบ text — การเก็บ UUID เป็น text ใน index ที่ร้อนคือความสิ้นเปลืองที่เจอบ่อยมาก
  • Row ของข้อมูล transactional ทั่วไป: 200 bytes – 1 KB; JSON blob ที่ใส่ทุกอย่างไว้ข้างใน: 2–20 KB
  • Thumbnail ที่ compress แล้ว: 10–50 KB; รูปจากมือถือ: 2–5 MB; วิดีโอ 1080p 1 นาที: ~50–100 MB
  • 1 ล้าน row × 500 bytes = 500 MB; 1 พันล้าน × 500 bytes = 500 GB — บวก index อีก 1.5–3 เท่าของ base
  • เลขยกกำลังสิบที่ต้องจำ: 1 วัน ≈ 86,400 s (ปัดเป็น 100k เวลาคิดในหัว), 1 เดือน ≈ 2.6 M s, 1 ปี ≈ 31.5 M s

ทางลัดที่ใช้ได้ทุกครั้ง

1 ล้านครั้งต่อวัน ≈ 12 ครั้งต่อวินาที (เพราะ 1,000,000 / 86,400 ≈ 11.6) จำเลขนี้ไว้ตัวเดียวก็ประมาณ QPS ได้เกือบทุกเคส

Back-of-envelope estimation

เป้าหมายไม่ใช่ความแม่นยำ เป้าหมายคือรู้ว่า design ของคุณห่างจากความจริงเกิน 10 เท่าไหม ภายใน 5 นาที ต่อหน้าคนอื่น

4 คำถาม

  1. QPS — average และ peak (build ตาม peak)
  2. Storage — ต่อวัน, ต่อปี, และหลังใช้ retention policy
  3. Bandwidth — เข้าและออก (egress มีค่าใช้จ่าย ingress มักไม่มี)
  4. Memory — จะเก็บ working set ไว้ใน cache กี่เปอร์เซ็นต์

ตัวอย่างเดินจริง: e-wallet ไทย

ประกาศสมมติฐานออกมาให้ชัด เพราะนั่นคือวินัยของการประมาณ: ผู้ใช้ลงทะเบียน 5 ล้านคน, active รายวัน 20% (1 ล้าน DAU), ต่อคนต่อวันเช็คยอด 3 ครั้ง และเคลื่อนย้ายเงิน 0.5 ครั้ง

READS (เช็คยอดเงิน)
  1,000,000 DAU x 3 = 3,000,000 reads/day
  average = 3,000,000 / 86,400 s  ≈  35 reads/s
  peak: traffic ไม่แบน แอปคนไทยพีคช่วงพักเที่ยงและ 19:00-22:00
        ใช้ peak factor 5x-10x
  peak ≈ 35 x 8  ≈  280 reads/s        <-- เล็กมาก DB ตัวเดียวเอาอยู่

WRITES (โอน, เติมเงิน, จ่ายบิล)
  1,000,000 x 0.5 = 500,000 txns/day  ≈  6 txns/s average
  peak ≈ 6 x 10  ≈  60 txns/s          <-- ยังเล็ก
  แต่: วันเงินเดือนออก (ราวๆ วันที่ 25) และวันเทศกาลเป็นสัตว์อีกชนิด
       เงินเดือนออก + campaign พร้อมกัน: สมมติ 30x average = 180 tps
  Design target: 300 tps sustained, burst 1,000 tps นาน 5 นาที

STORAGE (ledger entry — ส่วนที่คนลืม)
  Double-entry: 1 transfer = >=2 ledger rows (debit + credit)
  ถ้ามี fee กับ settlement leg มักกลายเป็น 4-6 rows
  500,000 txns/day x 5 rows x 400 bytes  ≈  1 GB/day
  x 365  ≈  365 GB/year ของ raw ledger ยังไม่รวม index
  Retention ตามกฎ (สมมติ 10 ปี — ต้อง CONFIRM กับ compliance)
  ≈ 3.6 TB + index ≈ 6-10 TB
  --> Postgres ตัวเดียวเก็บได้ แต่ serve ย้อนหลัง 10 ปีไม่สบาย
      สรุป: hot ledger (90 วัน) + archive tier

MEMORY (cache ยอดเงิน)
  1,000,000 active balances x 100 bytes ≈ 100 MB
  --> cache ได้สบาย แต่ยอดเงินคือเงิน ยอดเก่า 1 ครั้ง = ticket 1 ใบ
      ดูบทที่ 7 ว่าทำไมต้อง cache "read model" ไม่ใช่ยอดที่เป็น authoritative

5 นาทีนั้นซื้ออะไรให้คุณ

เลขคณิต 5 นาทีพิสูจน์แล้วว่า transaction rate ไม่ใช่ส่วนที่ยาก — relational database ตัวเดียวที่ tune ดีๆ รับ financial write 300 tps ได้ ส่วนที่ยากจริงคือ correctness (ห้าม double-spend, ห้ามเงินหาย), retention (ledger 10 ปี), และ peak concentration (วันเงินเดือนออก) ซึ่งเป็นบทสนทนาที่ต่างจาก "เรามี 1 ล้านผู้ใช้ ต้องใช้ microservices กับ Kafka" สิ้นเชิง — นี่คือทักษะที่ให้ผลตอบแทนสูงสุดในคอร์สนี้

Bandwidth — มิติที่ถูกลืมบ่อยที่สุด

ต่อจากตัวอย่างเดิม: ถ้าหน้า transaction history ตอบ JSON 20 รายการ ≈ 8 KB

EGRESS
  3,000,000 balance reads/day x 1 KB      ≈  3 GB/day
  500,000 history views/day x 8 KB        ≈  4 GB/day
  รวม ≈ 7 GB/day ≈ 210 GB/month
  --> ค่า egress ยังถูก แต่ถ้าเปลี่ยนเป็นส่ง statement PDF 200 KB
      ให้ 1 ล้านคนต่อเดือน = 200 GB/month เพิ่มมาจากจุดเดียว

Peak factor ที่คนคำนวณผิดบ่อย

  • Thundering herd จาก marketing — push notification ถึง 1 ล้านคนตอน 20:00 ทำให้พีคเกิดใน 60 วินาที ไม่ใช่กระจายทั่วชั่วโมง 1 ล้าน × open rate 8% ใน 1 นาที = ~1,300 req/s จากศูนย์ ตาราง campaign ของ marketing จะไม่โผล่ใน capacity plan ถ้าคุณไม่ไปถาม
  • Retry storm — client ที่ retry 3 ครั้งโดยไม่มี backoff เปลี่ยน outage 5 นาทีให้เป็นโหลด 4 เท่า ตอนที่คุณอ่อนแอที่สุด คูณ peak ด้วย retry policy ของคุณเสมอ
  • Cron alignment — ทุกอย่างที่ตั้งไว้ 00:00 จะรันตอน 00:00 พร้อมกัน — ใส่ jitter
  • Batch window — ไฟล์ settlement/clearing, การออก statement และงาน reconciliation ก็เป็นพีค เพียงแต่มันเกิดตอน 02:00 ที่ไม่มีใครดู dashboard

จาก estimation ไปเป็นข้อสรุปเชิงสถาปัตยกรรม

ตัวเลขมีประโยชน์เมื่อมันตัดทางเลือกออก ตารางนี้คือ mapping ที่ใช้ได้บ่อย

ถ้าตัวเลขบอกว่าข้อสรุปเชิงสถาปัตยกรรม
write < ~1,000 tps และ data < ~1 TBฐานข้อมูลเดียว + read replica พอ อย่า shard
read : write > 20 : 1ใส่ cache กับ read replica ก่อนคิดเรื่องอื่น
working set ใส่ RAM ได้ทั้งก้อนcache hit จะสูงมาก — จ่ายค่า RAM คุ้มกว่าเพิ่ม node
data โตเกิน 10 TB และ query แตะแต่ข้อมูลใหม่ทำ tiering (hot/warm/cold) ไม่ใช่ shard
peak / average > 20 เท่าต้องมี queue ซับแรง + autoscaling + load shedding
fan-out > 10 downstream ต่อ requesttail latency จะเป็นปัญหาหลัก ไม่ใช่ throughput
egress > หลาย TB/เดือนค่า network จะเริ่มแซงค่า compute — ดูบทที่ 17

Lab 2 — Calibrate ตัวเอง

ส่วน A. หยิบระบบที่คุณดูแลอยู่ โดยยังไม่เปิด dashboard เขียนตัวเลขที่คุณเดา: peak RPS ของ endpoint ที่ร้อนที่สุด, p50 และ p99 ของ endpoint นั้น, จำนวน row ในตารางใหญ่สุด, GB/day ของ log, ค่า infra ต่อเดือน

ส่วน B. เปิดดูของจริง แล้วให้คะแนนตัวเอง — ห่างไม่เกิน 2 เท่า = ดี, ไม่เกิน 10 เท่า = รับได้, เกิน 10 เท่า = คุณกำลังออกแบบแบบตาบอดในมิตินั้น

ส่วน C. ทุกมิติที่พลาดเกิน 10 เท่า เขียน 1 ประโยคว่าจะดู dashboard หรือ query ตัวไหนทุกสัปดาห์นับจากนี้

สรุปบทนี้

จำ order of magnitude ไม่ใช่ทศนิยม · คิด 4 มิติเสมอ (QPS, storage, bandwidth, memory) · build ตาม peak ไม่ใช่ average · ประกาศสมมติฐานออกมาให้คนในห้องแย้งได้ · และกำกับตัวเลขที่ยังไม่ได้วัดว่า illustrative ทุกครั้ง บทถัดไป คือลำดับขั้นที่ทำให้ทั้งหมดนี้กลายเป็นกระบวนการที่ทำซ้ำได้