Skip to content
Tayakorn
System Design Handbook

System Design Handbook

Scaling & Reliability — ขยายระบบและกำจัดจุดตาย

Vertical/Horizontal Scaling · Load Balancing · SPOF · Replication · ▶ ในคลิป 0:12:40 – 0:29:50

เมื่อทราฟฟิกโตจนเครื่องเดียวรับไม่ไหว มีทางเลือกสองทาง: ทำให้เครื่อง ใหญ่ขึ้น หรือทำให้เครื่อง เยอะขึ้น บทนี้ว่าด้วยการเลือกทาง, เครื่องมือที่ทำให้ "เยอะขึ้น" เป็นจริงได้ (load balancer) และหลักคิดที่สำคัญกว่าความเร็ว — ระบบต้องไม่ตายเพราะชิ้นส่วนเดียวพัง

3.1 Vertical vs Horizontal Scaling

Vertical (Scale Up) — เครื่องใหญ่ขึ้น4 CPU16GB32 CPU256GBแพงขึ้นแบบก้าวกระโดดง่าย ไม่ต้องแก้โค้ด — แต่มีเพดาน และยังเป็นเครื่องเดียว (SPOF)Horizontal (Scale Out) — เครื่องเยอะขึ้นServer 1Server 2Server 3+ เพิ่มได้เรื่อย ๆเครื่องธรรมดาหลายตัว — ไม่มีเพดาน แต่ต้องมีตัวกระจายงานLB
FIG 3.1 Scale up ง่ายแต่มีเพดานและยังพังพร้อมกันทั้งระบบ · Scale out ไร้เพดานแต่ต้องการ load balancer และแอปที่ stateless
มิติVertical (Scale Up)Horizontal (Scale Out)
วิธีเพิ่ม CPU/RAM เครื่องเดิมเพิ่มจำนวนเครื่อง
แก้โค้ดแทบไม่ต้องต้องทำแอปให้ stateless
เพดานมี — ฮาร์ดแวร์ใหญ่สุดในตลาดแทบไม่มี
ความทนทานยังเป็น SPOFเครื่องหนึ่งตาย ที่เหลือรับแทน
ราคาแพงขึ้นแบบ exponentialเพิ่มแบบ linear ตามจำนวนเครื่อง

3.2 Load Balancer — จราจรกลางของระบบ

Load balancer (LB) คือด่านหน้าที่รับทุก request แล้วกระจายไปยังเซิร์ฟเวอร์ที่ "ยังแข็งแรง" เบื้องหลัง ทำสองหน้าที่พร้อมกัน: กระจายโหลด ให้ไม่มีเครื่องไหนล้น และ ตรวจสุขภาพ (health check) เพื่อหยุดส่งงานให้เครื่องที่ตาย

ผู้ใช้ทุก requestLoad BalancerIP เดียวที่โลกเห็นServer 1 · แข็งแรงรับงานปกติServer 2 · แข็งแรงรับงานปกติServer 3 · ตาย ✕ถูกตัดออกจากวงhealth check ไม่ตอบ→ หยุดส่งงานทันทีกระจายตาม algorithm
FIG 3.2 LB เป็น IP เดียวที่ผู้ใช้เห็น เซิร์ฟเวอร์เบื้องหลังเพิ่ม/ลด/ตายได้โดยผู้ใช้ไม่รู้สึก — นี่คือหัวใจของ horizontal scaling

อัลกอริทึมกระจายโหลดที่ต้องรู้

อัลกอริทึมวิธีทำงานเหมาะกับ
Round Robinวนส่งทีละเครื่องตามคิว 1→2→3→1...เซิร์ฟเวอร์สเปกเท่ากัน งานแต่ละชิ้นหนักพอ ๆ กัน
Weighted Round Robinวนตามคิวแต่เครื่องแรงรับสัดส่วนมากกว่าเครื่องสเปกไม่เท่ากัน (ช่วงเปลี่ยนผ่านฮาร์ดแวร์)
Least Connectionsส่งให้เครื่องที่มีงานค้างน้อยที่สุดงานแต่ละชิ้นใช้เวลาไม่เท่ากัน (เช่นมี request ช้า ๆ ปน)
IP Hashhash IP ผู้ใช้ → ผู้ใช้เดิมไปเครื่องเดิมเสมอต้องการ session affinity โดยไม่มี shared store

LB ยังแบ่งตามชั้นที่ทำงาน: L4 (Transport) ดูแค่ IP/port — เร็วมาก ส่วน L7 (Application) อ่าน HTTP ได้ จึง route ตาม path ได้ (เช่น /api/video ไปเครื่องกลุ่มหนึ่ง /api/chat ไปอีกกลุ่ม) แลกกับ overhead ที่สูงกว่า

3.3 Single Point of Failure — ศัตรูหมายเลขหนึ่ง

SPOF คือชิ้นส่วนใดก็ตามที่ถ้าพังแล้วทั้งระบบหยุด วิธีไล่หามันคือชี้ไปที่ทุกกล่องในไดอะแกรมแล้วถามว่า "ถ้ากล่องนี้หายไปตอนนี้ ระบบยังเดินไหม" — คำตอบ "ไม่" ทุกจุดคือการบ้าน และยาแก้มีตัวเดียวคือ redundancy (มีสำรอง):

  • Web/App tier — มีหลายเครื่องหลัง LB อยู่แล้วจากการ scale out
  • Load balancer เองก็เป็น SPOF! — แก้ด้วย LB คู่ (active-passive) + failover อัตโนมัติ หรือใช้ DNS กระจายไปหลาย LB
  • Database — ตัวที่มักถูกลืม แก้ด้วย replication (ถัดไป)
  • ทั้ง Data Center — ระดับสูงสุด: กระจายหลาย Availability Zone / หลาย region

คำถามที่ตามมาจากการไล่ SPOF เสมอคือ "กำจัดจนระบบทนแค่ไหนถึงจะพอ" — วงการวัดค่านี้ด้วย "จำนวนเก้า" (nines) ของ availability:

Availabilityเรียกว่าดาวน์ได้สูงสุด/ปี
99%two nines~3.65 วัน
99.9%three nines~8.8 ชั่วโมง
99.99%four nines~52 นาที
99.999%five nines~5.3 นาที

ทุก "เก้า" ที่เพิ่ม = ต้นทุนและ redundancy ที่ต้องเพิ่มเป็นเท่าตัว — กำหนดเป้าตามความสำคัญของระบบก่อนเสมอ (payment gateway ไล่ 99.99%+ ขณะที่ระบบภายในอาจพอใจ 99.9%) แล้วออกแบบ redundancy ให้พอดีเป้า ไม่ใช่มากที่สุดเท่าที่ทำได้

3.4 Database Replication — ฐานข้อมูลก็ต้องมีตัวสำรอง

โครงสร้างมาตรฐานคือ Primary–Replica (หรือ leader–follower): เขียนที่ primary ตัวเดียว แล้วข้อมูลถูกส่งต่อ (replicate) ไปยัง replica หลายตัวซึ่งรับหน้าที่ อ่าน — ได้ประโยชน์สองต่อ: กระจายโหลดอ่าน (ระบบส่วนใหญ่อ่านมากกว่าเขียนหลายเท่า) และเป็นตัวสำรองเมื่อ primary ตาย

App Serversอ่าน + เขียนPrimaryรับ “เขียน” ทั้งหมดReplica 1รับ “อ่าน”Replica 2รับ “อ่าน”WRITEREADreplicate การเปลี่ยนแปลง(มี replication lag เล็กน้อย)ถ้า primary ตาย → เลื่อนขั้นreplica เป็น primary (failover)
FIG 3.3 Primary–Replica: เขียนที่เดียว อ่านได้หลายที่ และมีแผนสำรองเมื่อ primary ล่ม

3.5 Database Sharding — เมื่อ Replication ไม่พอ

Replication ที่เล่าไปแก้ปัญหาการ อ่าน ได้ดี แต่ไม่ช่วยเรื่อง เขียน หรือ พื้นที่เก็บข้อมูล เลย — เพราะเขียนได้ที่ primary ตัวเดียวเสมอ และทุก replica ต้องเก็บสำเนาข้อมูล "ทั้งหมด" เท่ากับ primary เมื่อข้อมูลใหญ่เกินดิสก์เครื่องเดียว หรือเขียนถี่เกินที่ primary ตัวเดียวรับไหว ทางออกคือ Sharding (horizontal partitioning): ตัดแถวของตารางเดียวกันออกเป็นส่วน ๆ กระจายไปอยู่คนละเครื่อง (shard) ที่แต่ละตัวรับทั้งอ่านและเขียนของส่วนตัวเองอย่างอิสระ

กลยุทธ์วิธีแบ่งข้อแลก
Range-basedแบ่งตามช่วงของ shard key เช่น user_id 1–1M → shard 1Query แบบช่วงทำง่าย แต่เสี่ยง "shard ร้อน" ถ้าข้อมูลใหม่กระจุกที่ shard ล่าสุด
Hash-basedhash(shard key) กระจายแถวให้เกลี่ยเท่า ๆ กันทุก shardกระจายสม่ำเสมอ แต่ query แบบช่วงทำไม่ได้ และเพิ่ม/ลด shard ต้อง reshuffle ข้อมูลจำนวนมาก (บรรเทาได้ด้วย consistent hashing)
Directory-basedมี lookup table กลางคอยจำว่า key ไหนอยู่ shard ไหนยืดหยุ่นที่สุด ย้ายข้อมูลทีละส่วนได้ แต่ lookup service เองต้องดูแลเรื่อง availability เพิ่มอีกชั้น

สรุปบทที่ 3 — Scale up ง่ายแต่มีเพดาน → scale out คือทางหลักของระบบใหญ่ โดยมีเงื่อนไขว่าแอปต้อง stateless · LB กระจายงาน + health check ตัดเครื่องตาย (Round Robin / Least Connections / IP Hash) · ไล่ฆ่า SPOF ทุกชั้น — อย่าลืมว่า LB และ DB ก็ตายได้ · DB ใช้ primary–replica เพื่อกระจายโหลดอ่านและรองรับ failover · เขียน/พื้นที่เก็บไม่พอค่อย sharding — แลกมาด้วย JOIN ข้าม shard ที่ยากขึ้นและ reshard ที่แพง

Read the full book