บทที่ 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, กำจัดคอขวดที่ใช้ร่วมกัน
Performancerequest 1 ใบเร็วแค่ไหน ที่หางp50 / p95 / p99 / p99.9cache, ลด hop, query ที่ดีกว่า, serialize น้อยลง
Availabilityในหน้าต่างเวลา 1 request สำเร็จกี่เปอร์เซ็นต์request สำเร็จ ÷ request ที่ valid (ดีกว่านับนาที uptime)redundancy, isolation, failover เร็ว, graceful degradation
Reliabilityมันทำสิ่งที่ถูกต้อง อย่างสม่ำเสมอ ตลอดเวลา ไหมincident เรื่องความถูกต้อง, ข้อมูลหาย, ธุรกรรมซ้ำ/หายidempotency, transaction, invariant, reconciliation, test
Durabilitycommit แล้วข้อมูลยังอยู่ไหมRPO; ความน่าจะเป็นที่ข้อมูลหายต่อปีreplication (synchronous สำหรับเงิน), backup ที่เคย restore จริง
Operabilityคนที่ง่วงที่สุดตอน 03:00 วินิจฉัยและแก้ได้ไหมMTTD, MTTR, ความครอบคลุมของ runbook, ความแม่นของ alertobservability, 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 — ไม่ใช่ค่าที่วัดจากระบบจริง:

กำลังวาด chart…

ถ้า κ=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 ชั้นที่ผิดคือการเผาเงินที่พบบ่อยที่สุดในงานวิศวกรรม ทำตามลำดับนี้:

  1. วัดการแตกตัวของ request ด้วย trace — เวลาไปอยู่ที่ไหนจริงๆ ปกติคือ: query ช้า 1 อัน, ลูปที่คุยกันมากเกินไป 1 จุด, หรือการเรียก third party 1 ครั้ง
  2. หา resource ที่อิ่มตัว — CPU, memory, disk IOPS, network, connection pool, thread pool, quota ของ third party, หรือ lock — มีแค่ 1 อย่างที่เป็นข้อจำกัดในเวลาหนึ่ง
  3. ปลดล็อกมัน แล้ววัดใหม่ — คอขวดจะย้าย นี่คือเรื่องปกติและหมายความว่าคุณกำลังคืบหน้า
  4. หยุดเมื่อผ่าน 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 budget100% − 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 เปลี่ยนการเถียงให้เป็นตัวเลขที่ตกลงกันไว้ก่อน