บทที่ 11 · Part 3 — Scale & Reliability
Scaling & SLO
Scaling axes 3 แกน, Amdahl/Universal Scalability Law, Little’s Law, availability math และ error budget
Scalability, availability และ reliability ถูกใช้สลับกันในห้องประชุมตลอด แต่มันคือคนละเรื่อง แค่ใช้คำให้ถูกความสับสนก็หายไปครึ่งหนึ่ง
จบบทนี้คุณจะ
- แยกคำ 6 คำได้: scalability, performance, availability, reliability, durability, operability
- ใช้ Little's Law ตั้งขนาด thread pool / connection pool ได้ และอธิบาย outage ที่ "ไม่ได้แก้อะไรเลย" ได้
- รู้ว่าทำไมเพิ่ม node แล้วช้าลงได้ (Universal Scalability Law)
- คำนวณ availability ของ critical path ที่ต่อกันเป็นอนุกรมได้ — และตัวเลขนั้นจะทำให้คุณตกใจ
- ตั้ง SLI/SLO และใช้ error budget เป็นเครื่องมือบริหารได้
คำ 6 คำที่ต้องแยก
| คำ | นิยาม | วัดด้วย | ซื้อได้ด้วย |
|---|---|---|---|
| Scalability | เพิ่ม resource แล้วรับโหลดได้มากขึ้นโดยไม่ต้องออกแบบใหม่ ได้ไหม | throughput ที่เป้า latency คงที่ และต้นทุนต่อหน่วยงาน | stateless, partitioning, กำจัดคอขวดที่ใช้ร่วมกัน |
| Performance | request 1 ใบเร็วแค่ไหน ที่หาง | p50 / p95 / p99 / p99.9 | cache, ลด hop, query ที่ดีกว่า, serialize น้อยลง |
| Availability | ในหน้าต่างเวลา 1 request สำเร็จกี่เปอร์เซ็นต์ | request สำเร็จ ÷ request ที่ valid (ดีกว่านับนาที uptime) | redundancy, isolation, failover เร็ว, graceful degradation |
| Reliability | มันทำสิ่งที่ถูกต้อง อย่างสม่ำเสมอ ตลอดเวลา ไหม | incident เรื่องความถูกต้อง, ข้อมูลหาย, ธุรกรรมซ้ำ/หาย | idempotency, transaction, invariant, reconciliation, test |
| Durability | commit แล้วข้อมูลยังอยู่ไหม | RPO; ความน่าจะเป็นที่ข้อมูลหายต่อปี | replication (synchronous สำหรับเงิน), backup ที่เคย restore จริง |
| Operability | คนที่ง่วงที่สุดตอน 03:00 วินิจฉัยและแก้ได้ไหม | MTTD, MTTR, ความครอบคลุมของ runbook, ความแม่นของ alert | observability, runbook, ปุ่มควบคุมที่ปลอดภัย, ความเรียบง่าย |
ความต่างที่สำคัญที่สุดใน fintech
ระบบที่ available แต่ไม่ reliable แย่กว่าระบบที่ล่ม
ถ้า wallet ของคุณหักเงินซ้ำตอนโหลดสูง คุณไม่ได้เจอ outage — คุณเจอ incident ที่แตะเงินลูกค้า, งาน reconciliation ค้าง, อาจมีหน้าที่รายงานต่อ regulator, และปัญหาความเชื่อมั่นที่อยู่นานกว่าการแก้ bug
เมื่อต้องแลกอย่างหนึ่งกับอีกอย่างในเรื่องการเคลื่อนย้ายเงิน ให้เลือก ความถูกต้อง และ fail closed — ปฏิเสธรายการด้วยข้อความที่ชัดเจน "กรุณาลองอีกครั้งในอีกสักครู่" มีต้นทุนต่ำกว่ายอดเงินที่ผิดมาก
แกนของการ scale และจุดที่มันหยุดทำงาน
| แกน | ทำอะไร | ข้อจำกัด |
|---|---|---|
| Vertical (เครื่องใหญ่ขึ้น) | ถูกประเมินค่าต่ำ — ไม่ต้องแก้โค้ด ไม่มีความซับซ้อนของ distributed system ได้ผลทันที instance บน cloud สมัยนี้ไปได้ไกลมาก (หลายร้อย core, RAM หลาย TB) | หยุดที่ instance ใหญ่สุด และ ไม่ได้กำจัด single point of failure — ทำอันนี้ก่อนระหว่างที่สร้างทางแก้จริง |
| Horizontal (เครื่องมากขึ้น) | ไม่จำกัดในทางทฤษฎี | ต้อง stateless ที่ชั้น service และ partition ที่ชั้นข้อมูล; จ่ายด้วย coordination และความซับซ้อน |
| Functional (แยกตามความสามารถ) | ย้าย reporting ออกจาก transaction database; แยก read path จาก write path | มักเป็นชัยชนะใหญ่ที่ถูกที่สุด เพราะ workload ต่างชนิดต้องการ resource ต่างกัน |
| ทำงานน้อยลง | เทคนิคที่ได้ผลที่สุดและไม่เท่ที่สุด — กำจัด N+1 query, batch การเรียก, ตัด field ที่ไม่มีใครอ่าน, cache ผลที่คำนวณแล้ว, เลิก log 3 KB ต่อ request, ทำแบบ async, หรือทำชั่วโมงละครั้งแทนที่จะทำทุก request | วัดก่อน — query 3 อันดับแรกมักกินโหลดส่วนใหญ่ |
เริ่มจาก "ทำงานน้อยลง" ทุกครั้ง
การเพิ่ม node เพิ่มค่าใช้จ่ายทุกเดือนตลอดไป การลบ N+1 query ออก 1 จุด ให้ผลเท่ากันโดยไม่มีค่าใช้จ่ายเพิ่มเลย ถามตัวเองก่อนเสมอ: "งานนี้จำเป็นต้องเกิดขึ้นเลยไหม"
Little's Law — สูตรที่อธิบายความเจ็บปวดของคิว
L = λ × W concurrency = อัตราการมาถึง × เวลาที่อยู่ในระบบ
ตัวอย่าง: 500 req/s แต่ละใบใช้ 200 ms
L = 500 × 0.2 = 100 request ที่กำลังทำงานพร้อมกัน
แปลว่าคุณต้องมี worker/thread/connection อย่างน้อย 100 ตัว
ถ้ามี 50 ตัว request จะเข้าคิวและ latency จะไต่ขึ้น
โดยที่ traffic ไม่ได้เปลี่ยนเลยแม้แต่นิดเดียว
ตอนนี้ dependency ช้าลง: W จาก 200 ms -> 2 s
L = 500 × 2 = 1,000 request พร้อมกัน
pool 100 ตัวของคุณเล็กเกินไป 10 เท่าทันที
ทุกอย่างเข้าคิว timeout ยิง client retry อัตราการมาถึงเพิ่ม W เพิ่มอีก
นี่คือกลไกเบื้องหลัง outage ที่ "เกิดขึ้นทันที" ส่วนใหญ่ —
ไม่มี traffic spike เลย มีแค่ latency ที่เพิ่มขึ้น
ใช้ Little's Law ทำอะไร
ตั้งขนาด thread pool, DB connection pool, จำนวน worker และใช้อธิบายผู้จัดการว่าทำไม "เราไม่ได้แก้อะไรเลย" ก็ยังเป็นความผิดของเราได้
และข้อสำคัญ: connection pool คือ concurrency limit เพราะฉะนั้นเลือกค่ามันอย่างตั้งใจ อย่าปล่อยเป็น default
Universal Scalability Law — ทำไม node 2 เท่าไม่ได้ throughput 2 เท่า
Throughput ไม่โตเป็นเส้นตรง มี 2 แรงที่ต้านคุณ:
| แรง | คืออะไร | ผลต่อกราฟ |
|---|---|---|
| Contention (σ) | ส่วนที่ทำงานเรียงลำดับ — lock, single writer, แถวที่ใช้ร่วมกัน 1 แถว, leader node 1 ตัว | กราฟแบนลง |
| Coherency (κ) | ต้นทุนของการทำให้ N node ตรงกัน — cross-talk, cache invalidation, consensus, gossip | กราฟโก่งลง — เพิ่ม node แล้วช้าลง |
USL เขียนเป็นสมการ C(N) = N / [1 + σ(N−1) + κN(N−1)] โดย C(N) คือ
throughput เทียบกับ node เดียว ตัวอย่าง illustrative ด้านล่างใช้ σ=0.03
และ κ=0.002 เพื่อให้เห็นรูปร่างของ curve — ไม่ใช่ค่าที่วัดจากระบบจริง:
ถ้า κ=0 curve จะเพียงแบนลงจาก contention แต่ไม่ตก เมื่อ κ>0 term
κN(N−1) โตแบบกำลังสองและสุดท้ายกินประโยชน์จาก node ใหม่ทั้งหมด
ผลในทางปฏิบัติ
มีขนาด cluster ที่เหมาะสมที่สุดสำหรับ design หนึ่งๆ เกินจากนั้นการเพิ่ม node ทำให้แย่ลง
ถ้าเคยเจอว่าเพิ่ม capacity แล้วสถานการณ์แย่ลง คุณเจอ κ เข้าไปแล้ว ทางแก้ไม่ใช่เพิ่ม node — คือกำจัด coordination (partition ข้อมูลให้ node ไม่ต้องตกลงกันอีก)
อ้างอิง: Universal Scalability Law ของ Neil Gunther · blog ของ Marc Brooker สำหรับคำอธิบายเรื่อง scaling และพฤติกรรมของคิวในระบบจริง
หาคอขวดก่อนจะ scale อะไร
การ scale ชั้นที่ผิดคือการเผาเงินที่พบบ่อยที่สุดในงานวิศวกรรม ทำตามลำดับนี้:
- วัดการแตกตัวของ request ด้วย trace — เวลาไปอยู่ที่ไหนจริงๆ ปกติคือ: query ช้า 1 อัน, ลูปที่คุยกันมากเกินไป 1 จุด, หรือการเรียก third party 1 ครั้ง
- หา resource ที่อิ่มตัว — CPU, memory, disk IOPS, network, connection pool, thread pool, quota ของ third party, หรือ lock — มีแค่ 1 อย่างที่เป็นข้อจำกัดในเวลาหนึ่ง
- ปลดล็อกมัน แล้ววัดใหม่ — คอขวดจะย้าย นี่คือเรื่องปกติและหมายความว่าคุณกำลังคืบหน้า
- หยุดเมื่อผ่าน SLO พร้อมมี headroom — การไล่หาผลลัพธ์เพิ่มโดยไม่มี requirement คืองานอดิเรก ไม่ใช่วิศวกรรม
Availability math — ตัวเลข nine ราคาเท่าไหร่
| เป้า | Downtime/เดือน | Downtime/ปี | ต้องมีอะไร |
|---|---|---|---|
| 99% | ~7.2 ชั่วโมง | ~3.65 วัน | server 1 ตัวกับความตั้งใจดี |
| 99.9% (พบบ่อยสุด) | ~43 นาที | ~8.8 ชั่วโมง | multi-AZ, automated failover, on-call, deploy ที่ทดสอบแล้ว |
| 99.95% | ~22 นาที | ~4.4 ชั่วโมง | ข้างบน + canary deploy + runbook ที่ซ้อมแล้ว + ไม่มี manual failover |
| 99.99% | ~4.4 นาที | ~53 นาที | active-active ข้าม zone, ไม่มี maintenance window, อัตโนมัติทุกอย่าง, องค์กรที่โต — แพง |
| 99.999% | ~26 วินาที | ~5.3 นาที | จริงๆ ทำได้เฉพาะ component ที่แคบ เรียบง่าย และมีงบเยอะ — อย่าสัญญาตัวเลขนี้ให้ทั้ง product |
Dependency ที่ต่อเป็นอนุกรมจะคูณกัน — กับดักในทุก microservice design
เส้นทาง request: gateway -> auth -> account -> ledger -> rail
แต่ละตัว available 99.9% และ *ทุกตัว* ต้องทำงาน:
0.999^5 = 0.995 -> 99.5% -> ล่มประมาณ 3.6 ชั่วโมง/เดือน
คุณสัญญา 99.9% แต่คุณสร้าง 99.5%
ไม่มีใครทำอะไรผิด — เลขคณิตทำให้เป็นอย่างนั้น
5 ทีมที่รอบคอบผลิตตัวเลขที่แย่ออกมา
ทางแก้ 4 ทาง เรียงตามคุณค่า:
คำว่า "อิสระ" ในข้อ 3 ทำงานหนักมาก
Replica 2 ตัวที่อยู่ rack เดียวกัน, AZ เดียวกัน, ได้ config push เดียวกัน, หรือใช้ certificate ใบเดียวกัน ไม่ใช่ตัวแปรอิสระ เลข 99.9999% เป็นนิยาย — ดู correlated failure ในบทที่ 12
SLI, SLO, SLA และ error budget
| คำ | คืออะไร | ตัวอย่าง |
|---|---|---|
| SLI | ตัววัด | "สัดส่วนของ POST /transfers ที่ตอบไม่ใช่ 5xx ภายใน 800 ms วัดที่ gateway" |
| SLO | เป้าภายในของคุณสำหรับ SLI นั้น | "99.9% ในหน้าต่างเลื่อน 28 วัน" — เป็นคำมั่นทางวิศวกรรม |
| SLA | สัญญากับลูกค้าหรือ partner พร้อมค่าปรับ | ต้องหลวมกว่า SLO เสมอ — คุณต้องมีที่ให้ล้มภายในโดยไม่ต้องจ่ายเงิน |
| Error budget | 100% − SLO | ที่ 99.9% คุณล้มได้ 0.1% ของ request — งบนี้เป็นทรัพยากรที่คุณมีสิทธิ์ใช้ |
ทำไม error budget เป็นเครื่องมือบริหารที่มีประโยชน์ที่สุดที่คุณจะได้เรียน
มันจบสงครามชั่วนิรันดร์ระหว่าง "ปล่อยของให้เร็วขึ้น" กับ "หยุดทำของพัง" ด้วยการทำให้เป็นตัวเลขที่ทั้ง 2 ฝ่ายยอมรับล่วงหน้า
- งบเหลือ → ปล่อยของ เสี่ยงได้ รัน migration ได้ ทำ chaos experiment ได้
- งบหมด → งาน feature หยุด งาน reliability มาก่อนจนหน้าต่างเลื่อนไป — ตกลงกันก่อนเกิด incident ไม่ใช่เถียงกันตอนเกิด
- งบไม่เคยถูกใช้เลย → SLO หลวมเกินไป หรือคุณลงทุนเรื่อง reliability มากกว่าที่ธุรกิจต้องการ — อันนี้ก็เป็นข้อค้นพบเหมือนกัน
Alert ที่ burn rate ไม่ใช่ที่จำนวน error
ไม่ actionable ตอน 03:00:
"error rate อยู่ที่ 0.4%"
actionable ตอน 03:00:
"ที่อัตรานี้ งบ 28 วันจะหมดในอีก 4 ชั่วโมง"
Multi-window burn-rate alert (จาก SRE Workbook):
fast burn: 14.4x budget ใน 1 ชั่วโมง -> page ทันที
slow burn: 6x budget ใน 6 ชั่วโมง -> ticket
--> 2 หน้าต่างกันการ alert ปลอมจากสัญญาณสั้นๆ
เลือก SLI ต่อ user journey ไม่ใช่ต่อ server
การส่งคำสั่งจ่ายเงิน, การดูยอดเงิน, การ login, การดาวน์โหลด statement — แต่ละอย่างได้ SLO ของตัวเอง เพราะมูลค่าทางธุรกิจต่างกัน
การรัน payment path ที่ 99.95% และตัวสร้าง statement PDF ที่ 99% เป็นเรื่องที่สมเหตุสมผลมาก
Lab 11 — SLO และ availability math
ส่วน A — นิยาม (40 นาที) สำหรับ service ที่คุณดูแล เขียน SLI 3 ตัวพร้อมนิยามที่แม่น (อะไรนับเป็น valid request? วัดที่ไหน? อะไรคือ "สำเร็จ"?) ตั้ง SLO ให้แต่ละตัวพร้อมหน้าต่างเลื่อน คำนวณ error budget ต่อเดือนทั้งเป็นนาทีและเป็นจำนวน request
แล้ว คำนวณ availability แบบอนุกรมของ critical path จาก component แต่ละตัว — ทีมส่วนใหญ่ตกใจกับตัวเลขนี้
ส่วน B — ซักไซ้ (30 นาที) ลิสต์ทุก dependency บน critical path แต่ละตัวตอบว่า: จำเป็นจริงไหมที่ request จะสำเร็จ? ตอนนี้ถ้ามันล่มจะเกิดอะไร? ควรเกิดอะไร? ทำตาราง degradation ออกมา และระบุว่าแถวไหนต้องให้เจ้าของธุรกิจตัดสิน ไม่ใช่คุณตัดสิน
สรุปบทนี้
available ≠ reliable และในเรื่องเงินให้เลือก reliable แล้ว fail closed · เริ่มด้วย "ทำงานน้อยลง" ก่อน vertical ก่อน horizontal · Little's Law อธิบาย outage ที่ไม่มี traffic spike · coordination ทำให้เพิ่ม node แล้วช้าลง · dependency อนุกรมคูณกัน 0.999^5 = 99.5% · และ error budget เปลี่ยนการเถียงให้เป็นตัวเลขที่ตกลงกันไว้ก่อน