Large-Scale System Architecture
บทที่ 1-6 คือ "ขนาน" ในความหมายดั้งเดิม — ทำงานเดียวให้เร็วขึ้น จากนี้ไปคือ "ขนาน" ในอีกความหมาย: รับผู้ใช้หลายล้านคนพร้อมกันให้ระบบยังตอบสนองไวและไม่ล่ม บทนี้เริ่มจากสถาปัตยกรรมพื้นฐานที่ทำให้เว็บเซอร์วิสขนาดใหญ่ทำงานได้จริง
1ความต้องการหลักของ Internet Service ขนาดใหญ่
2Multi-Tier Architecture
Web Browser --request--> Web Server --search.jsp+params--> AppServer --> Database Web Browser <--response(HTML page)--
3Stateful เทียบกับ Stateless Server
| ประเภท | ลักษณะ | ผลกระทบ |
|---|---|---|
| Stateful Server | เก็บข้อมูล "session" ของแต่ละการเชื่อมต่อไว้ — request ปัจจุบันอาจขึ้นกับ request ก่อนหน้า (ตัวอย่าง: database server, FTP) | ใช้ทรัพยากรเซิร์ฟเวอร์ต่อเนื่อง (memory, TCP port) จำกัดจำนวน client ที่รองรับได้ และถ้า connection ขาด บริการก็ขาดตามไปด้วย |
| Stateless Server | ไม่เก็บข้อมูลของแต่ละ connection — แต่ละ request เป็นอิสระจากกันสมบูรณ์ (ตัวอย่าง: web server ทั่วไป, NFS) | ใช้ทรัพยากรเซิร์ฟเวอร์น้อยกว่า รองรับ client จำนวนมากได้ดีกว่า — เป็นคุณสมบัติที่ทำให้ scale แนวนอน (horizontal scaling) ทำได้ง่าย เพราะ request ไหนไปตกที่เครื่องไหนก็ได้ผลลัพธ์เหมือนกัน |
4Server Load Balancing: กระจายงานไปหลายเครื่อง
| วิธี | วิธีการ | ข้อดี/ข้อเสีย |
|---|---|---|
| DNS Round-Robin | DNS server ตอบ IP ต่างกันสลับกันไปเมื่อถูกถามชื่อเดียวกันซ้ำ ๆ — client ที่ต่างกันได้ IP ของเซิร์ฟเวอร์ต่างกัน | ข้อดี: ราคาถูก ทำได้ง่าย ข้อเสีย: กระจาย load ได้อย่างเดียว ไม่ได้ high availability จริง (ไม่รู้ว่าเซิร์ฟเวอร์ปลายทางยังทำงานอยู่ไหม) และมีปัญหากับ DNS caching ที่ client บางตัวจำ IP เก่าค้างไว้ |
| Reverse Proxy | Client ยิง request มาที่ proxy ตัวเดียว proxy กระจายงานต่อไปยัง server จริงเบื้องหลัง — client ไม่รู้จัก IP ของ server จริงเลย | ซ่อนโครงสร้างภายในได้ดี รวม TLS termination และ caching ไว้ที่จุดเดียวได้ |
| Hardware Load Balancer | อุปกรณ์เฉพาะทางกระจาย request ไปยัง server จริง — client เห็นแค่ "virtual IP" เดียว ไม่รู้ว่าเบื้องหลังมีกี่เครื่อง | ควบคุมได้ละเอียดสุด (เช่น health check ตัดเครื่องที่ตายออกอัตโนมัติ) แต่แพงและเป็นจุดเดียวที่ต้องดูแลเรื่อง availability |
5Web Caching: ใช้ประโยชน์จาก Locality
t ลดลงมหาศาล| ตำแหน่งของ Cache | ตัวอย่าง |
|---|---|
| Client-side (Browser Cache) | เก็บไว้ที่เครื่องผู้ใช้เอง — เร็วที่สุด แต่ใช้ได้เฉพาะผู้ใช้คนเดียว |
| Proxy Cache | เก็บไว้ที่ตัวกลางระหว่าง client กับ server — ใช้ร่วมกันได้หลาย client ในองค์กร/ISP เดียวกัน |
| Server-side Cache | เก็บไว้ใกล้ origin server — ลดภาระของ backend/database โดยตรง |
6จาก Reverse Proxy ไปสู่ CDN และ Service Mesh
- อธิบายว่าทำไม stateless server จึง scale แนวนอนได้ง่ายกว่า stateful server
- เปรียบเทียบ DNS Round-Robin, Reverse Proxy, และ Hardware Load Balancer ในแง่ต้นทุนและความสามารถ
- อธิบายสูตร t = latency + size/bandwidth และเชื่อมโยงกับเหตุผลที่ web caching ช่วยลดเวลาตอบสนอง
- อธิบายว่า Zipf's Law เกี่ยวข้องกับประสิทธิภาพของ web caching อย่างไร
- อธิบายว่า CDN และ Service Mesh สืบทอดแนวคิดจาก web caching และ reverse proxy แบบดั้งเดิมอย่างไร และทำไมระบบยุคปัจจุบันจึงต้องการเครื่องมือที่ซับซ้อนกว่าเดิม
7Scale Up กับ Scale Out
| แนวทาง | วิธี | ข้อดี | เพดาน |
|---|---|---|---|
| Scale up | เพิ่ม CPU, RAM, disk หรือ accelerator ให้เครื่องเดิม | โปรแกรมเปลี่ยนน้อย latency ภายในเครื่องต่ำ | เครื่องใหญ่ราคาโตเร็ว มีขีดจำกัดและยังเป็น fault domain เดียว |
| Scale out | เพิ่มเครื่องและกระจายงาน | ขยายทีละส่วนและทนเครื่องเสียได้ | ต้องจัด placement, communication, consistency และ failure |
ระบบจริงใช้ทั้งสองแบบ Scale up จนถึงจุดคุ้มค่าเพื่อเลี่ยง distribution ที่ไม่จำเป็น แล้ว scale out เมื่อ workload, availability หรือ fault isolation ต้องการ การเริ่มด้วย microservices หลายร้อยตัวก่อนมีปัญหา scale อาจสร้างต้นทุนมากกว่าประโยชน์
8Control Plane กับ Data Plane
Data plane รับ traffic และประมวลผลคำขอจริง ส่วน control plane เก็บ configuration, placement, routing policy และสถานะสมาชิก การแยกสองส่วนช่วยให้เส้นทางข้อมูลไม่ต้องถามศูนย์กลางทุก request แต่ control plane ต้องรักษาความถูกต้องสูง
Kubernetes control plane, service discovery และ load-balancer configuration ล้วนใช้รูปแบบนี้ การออกแบบต้องระบุว่า data plane ทำงานต่อได้นานเพียงใดเมื่อ control plane ติดต่อไม่ได้ และ configuration เก่าปลอดภัยหรือไม่
9Reverse Proxy, Load Balancer และ API Gateway
| องค์ประกอบ | หน้าที่หลัก | สิ่งที่มักเพิ่ม |
|---|---|---|
| Reverse proxy | รับแทน backend และส่งต่อ request | TLS termination, compression, cache และ routing |
| Load balancer | กระจาย connection/request ไปหลาย instances | Health check, draining และ failover |
| API gateway | จุดเข้าของ API หลาย services | Authentication, quota, transformation และ aggregation |
ผลิตภัณฑ์หนึ่งอาจทำได้ทุกบทบาท แต่การแยกแนวคิดช่วยเห็น failure domain หาก gateway รวม logic มากเกินไป มันกลายเป็นทั้งคอขวดและ monolith หน้าบ้าน แม้ backend จะแยกเป็น services แล้ว
10Load-Balancing Algorithms
| Algorithm | เหมาะกับ | ข้อควรระวัง |
|---|---|---|
| Round robin | Instances และ request cost ใกล้กัน | ไม่เห็นงานค้างและ connection ยาว |
| Least connections | Connection duration ต่างกัน | Connection count ไม่เท่ากับ CPU cost |
| Weighted | เครื่องความจุต่างกันหรือ canary rollout | Weight ต้องปรับตาม capacity จริง |
| Consistent hashing | ต้องการ affinity ตาม key และลด remap | Hot key และ node imbalance |
| Power of two choices | Cluster ใหญ่ ต้องการตัดสินใจง่ายแต่ balance ดี | ต้องมี load signal ที่สดพอ |
Load balancer ที่เลือก backend เร็วไม่ได้ช่วยหากทุก backend เรียก database ตัวเดียว คอขวดเพียงย้ายลงชั้นล่าง ต้องวัด dependency ทั้งสาย ไม่ใช่ดู CPU web tier อย่างเดียว
11Health Check และการนำ Instance เข้า–ออก
Liveness บอก process ยังตอบหรือไม่ Readiness บอกพร้อมรับ traffic หรือยัง Instance อาจ alive แต่ database pool เต็มหรือกำลัง warm cache จึงไม่ควรรับงานใหม่
- อย่าให้ health check พึ่ง downstream ทุกตัวจน downstream สะดุดแล้ว backend ทั้งหมดถูกถอดพร้อมกัน
- ใช้ failure threshold ลดผลจาก packet loss ชั่วคราว
- ทำ connection draining ก่อนปิด instance ให้ request ที่กำลังทำจบ
- แยก startup probe สำหรับโปรแกรมเริ่มช้า
- ตรวจจากมุมผู้ใช้ภายนอกเพิ่มเติม ไม่เชื่อ self-report เพียงอย่างเดียว
12Stateless ไม่ได้แปลว่าไม่มี State
Stateless server หมายถึง request ถัดไปไม่จำเป็นต้องกลับ instance เดิม เพราะ state ที่ต้องใช้ถูกส่งมากับ request หรือเก็บในบริการภายนอก แต่ระบบรวมยังมี state อยู่ใน database, cache, token และ queue
Sticky session ลดการย้าย state และใช้ local cache ได้ดี แต่ failover กับ rebalance ยาก ควรใช้เมื่อมีเหตุผล เช่น WebSocket หรือ upload session และออกแบบ recovery เมื่อ instance หาย
13Cache อยู่ตรงไหนได้บ้าง
| ตำแหน่ง | ลดต้นทุนใด | ปัญหาหลัก |
|---|---|---|
| Browser/client | Network ทั้งเส้นและ server load | ควบคุม invalidation ยากและ privacy |
| CDN/edge | ระยะทางและ origin bandwidth | Cache key, purge และ personalized content |
| Reverse proxy | Backend request | Header semantics และ stale response |
| Application local | Network call และ serialization | สำเนาต่อ instance ไม่ตรงกัน |
| Distributed cache | Database load | Hot key, stampede และ availability |
Cache hit ratio สูงไม่ได้รับประกันว่าคุ้ม หาก miss ทุกครั้งเป็น query แพงและเกิดพร้อมกันช่วง TTL หมด ระบบยังล่มได้ ต้องดู miss cost และ distribution ของ key ด้วย
14Cache Invalidation และความสดของข้อมูล
กลยุทธ์ทั่วไปมี TTL, explicit invalidation, versioned key และ event-driven update TTL ง่ายแต่ยอม stale ตามช่วงเวลา Explicit invalidation สดกว่าแต่ message อาจหาย Versioned asset เหมาะกับ immutable content เพราะชื่อใหม่เมื่อข้อมูลเปลี่ยน
15Cache Stampede และ Hot Key
เมื่อ key ยอดนิยมหมดอายุ requests จำนวนมาก miss พร้อมกันและวิ่งไป origin เรียกว่า stampede วิธีรับมือได้แก่ request coalescing ให้หนึ่ง request refresh ส่วนที่เหลือรอ, stale-while-revalidate, probabilistic early refresh และ jitter TTL
Hot key อาจอิ่มแม้กระจาย cache หลาย nodes เพราะ consistent hashing ส่ง key เดียวไป node เดียว อาจ replicate key, local cache เพิ่มชั้น หรือแบ่งข้อมูลที่ aggregate ได้ แต่ต้องระวังความสดและ invalidation เพิ่มขึ้น
16Asynchronous Messaging: แยกเวลาของผู้ส่งกับผู้รับ
Queue ให้ producer ฝากงานแล้วตอบกลับโดยไม่รอ consumer ทำเสร็จ ช่วยรับ burst และแยก scaling แต่เปลี่ยน semantics ผู้ใช้ต้องรู้ว่างาน “รับแล้ว” ไม่เท่ากับ “เสร็จแล้ว”
- กำหนด retry และ dead-letter queue
- ทำ consumer idempotent เพราะ message อาจส่งซ้ำ
- วัด queue age ไม่ใช่เพียง queue length
- ใช้ backpressure หรือ admission control เมื่อ backlog โต
- กำหนด ordering scope เช่นต่อ key/partition ไม่ใช่หวัง global order ฟรี
17Backpressure และ Load Shedding
เมื่อ downstream รับไม่ทัน ระบบมีสามทาง: queue, ชะลอ upstream หรือปฏิเสธบางงาน Queue เหมาะกับ burst ชั่วคราว Backpressure ควบคุมอัตรา และ load shedding รักษางานสำคัญเมื่อ capacity ไม่พอ
การตอบ 429/503 อย่างรวดเร็วพร้อม Retry-After อาจดีกว่ารับทุก request แล้ว timeout หลัง 30 วินาที เพราะเก็บ resources ไว้ให้งานที่มีโอกาสสำเร็จ ระบบที่ “ตอบทุกคน” แต่ตอบช้าเกิน deadline ไม่ได้ available ในความหมายของผู้ใช้
18Timeout, Retry และ Circuit Breaker
| กลไก | เป้าหมาย | ความเสี่ยง |
|---|---|---|
| Timeout | จำกัดเวลาที่ resource ถูกผูกไว้ | สั้นเกินเกิด false failure ยาวเกินทำให้ค้างสะสม |
| Retry | ผ่าน transient failure | ขยายโหลดและทำ side effect ซ้ำ |
| Circuit breaker | หยุดเรียกปลายทางที่ล้มต่อเนื่อง | ตั้ง threshold ผิดอาจปิดบริการที่ยังฟื้นได้ |
| Bulkhead | แยก resource pool ไม่ให้ failure ลาม | Capacity แต่ละ pool ใช้แทนกันไม่ได้ |
Retry ต้องมี deadline รวม, exponential backoff และ jitter หากทุกชั้น retry สามครั้ง call chain สามชั้นอาจขยายงานมากกว่าที่คิด ควรกำหนด ownership ของ retry ให้ชัด
19Tail Latency และ Fan-Out
หน้าเดียวอาจเรียกบริการ 20 ตัวและรอทั้งหมด แม้แต่ละตัวมี p99 เพียง 1% โอกาสที่สักตัวช้าจะสูงขึ้นเมื่อ fan-out มาก ระบบจึงใช้ deadline propagation, partial response, hedged request อย่างระวัง และลด dependency บน critical path
20Partitioning: แบ่งผู้ใช้และข้อมูล
เมื่อ backend หรือ database ตัวเดียวไม่พอ ต้อง partition ตาม key เช่น user ID, tenant หรือ region Hash กระจายสม่ำเสมอแต่ query ตามช่วงยาก Range partition รองรับ range query แต่เกิด hotspot ตามเวลาได้
Shard key ที่ดีต้องมี cardinality สูง กระจายโหลด และตรงกับ access pattern การเลือก country เป็น key อาจทำให้ประเทศใหญ่ร้อน ส่วน timestamp ทำให้ข้อมูลใหม่ทั้งหมดลง shard ล่าสุด
21Service Decomposition และต้นทุนของ Remote Call
แยก service ช่วย ownership และ scaling คนละอัตรา แต่ local function call กลายเป็น network call ที่มี serialization, timeout, retry และ partial failure Call chain ยาวเพิ่ม tail และทำ transaction ยากขึ้น
22Service Discovery และ Configuration
Instances เข้าออกและ IP เปลี่ยน Client ต้องค้นชื่อ service ไปยัง healthy endpoints ผ่าน DNS, registry หรือ platform service การ cache รายชื่อช่วยลด lookup แต่ stale endpoint ทำให้ retry เพิ่ม
Configuration ควร version, audit และ rollout เป็นขั้น แทนการแก้ทุกเครื่องพร้อมกัน Feature flag ช่วยแยก deploy code จากเปิด behavior แต่ flag จำนวนมากกลายเป็น state ที่ต้องทดสอบและเก็บกวาด
23Autoscaling: เพิ่มเครื่องหลังเห็นโหลดอาจช้าเกินไป
Reactive autoscaling ใช้ CPU, queue หรือ latency แต่ instance มี startup/warm-up delay หาก workload พุ่งเร็วกว่าการเพิ่ม capacity ระบบอาจล้มก่อน scale เสร็จ Predictive scaling ใช้รูปแบบเวลา ส่วน scheduled scaling เหมาะกับเหตุการณ์รู้ล่วงหน้า
- เลือก metric ที่สัมพันธ์กับ capacity เช่น concurrency หรือ queue age
- กำหนด scale-out เร็วและ scale-in ช้าลด oscillation
- เผื่อ warm cache และ connection pool
- ใช้ maximum limit ป้องกัน bug เผางบไม่จำกัด
- ทดสอบว่าชั้น database/cache scale ตามหรือไม่
24Deployment โดยไม่หยุดระบบ
| วิธี | แนวคิด | ข้อดี/ต้นทุน |
|---|---|---|
| Rolling | เปลี่ยนทีละชุด | ใช้ capacity เพิ่มไม่มาก แต่มีสองเวอร์ชันร่วมกัน |
| Blue–green | เตรียมชุดใหม่ทั้งชุดแล้วสลับ traffic | Rollback เร็วแต่ใช้ resource สองชุด |
| Canary | ส่ง traffic ส่วนน้อยไปเวอร์ชันใหม่ | ลด blast radius แต่ต้องมี metric และ routing ดี |
Schema และ API ต้อง backward-compatible ระหว่างช่วง deploy มิฉะนั้น rolling update ที่ application ปลอดภัยอาจพังตรง database contract
25Observability: Metrics, Logs และ Traces
Metrics เห็นแนวโน้ม Logs ให้รายละเอียดเหตุการณ์ Traces เชื่อมเส้นทาง request ข้าม services การมีข้อมูลจำนวนมากไม่เท่ากับสังเกตระบบได้ ต้องเชื่อมข้อมูลกับคำถามและ SLO
- วัด request rate, errors, duration และ saturation
- แนบ correlation/trace ID ผ่านทุกชั้น
- แยก queue time จาก service time
- เก็บ deployment/config version เพื่อเทียบเหตุการณ์
- ระวัง high-cardinality labels ทำระบบ metrics แพง
26กรณีศึกษา: หน้า Feed ที่โตจากหนึ่งเครื่อง
- เริ่ม monolith กับ database เดียวเพื่อเรียนรู้ access pattern
- เพิ่ม reverse proxy และ stateless application replicas
- ใช้ CDN กับรูป/asset และ cache feed fragments
- แยกงาน fan-out/notification ผ่าน queue
- Partition ผู้ใช้เมื่อ database capacity ถึงเพดาน
- เพิ่ม observability, admission control และ graceful degradation
เส้นทางนี้ไม่ได้บอกว่าทุกระบบต้องเดินเหมือนกัน แต่ชี้ว่าควรเพิ่มความซับซ้อนเมื่อมีหลักฐานของคอขวด แต่ละขั้นสร้าง dependency ใหม่ที่ต้องรับผิดชอบ
27แล็บและแบบฝึกที่แนะนำ
28Rate Limiting และ Fairness
Rate limiter ป้องกันผู้ใช้หรือ tenant หนึ่งใช้ capacity ทั้งหมด Token bucket ยอม burst ภายในจำนวน token แล้วจำกัดอัตราเฉลี่ย Leaky bucket ทำให้อัตราออกเรียบ ส่วน concurrency limit จำกัดงานที่อยู่ในระบบพร้อมกัน
Distributed limiter ต้องเลือกระหว่าง counter กลางที่แม่นแต่เพิ่ม latency กับ quota ต่อ instance ที่เร็วแต่คลาดเคลื่อนชั่วคราว ควรระบุว่า limit เป็น hard guarantee หรือ best effort และตอบผู้ใช้ด้วยสถานะกับ Retry-After ที่เหมาะสม
29Graceful Degradation
เมื่อ dependency ช้าหรือ capacity ไม่พอ ระบบไม่จำเป็นต้องล้มทั้งหน้า อาจซ่อน recommendation ใช้ข้อมูล cache ที่เก่ากว่า ลด resolution หรือรับงานไว้ทำภายหลัง Feature สำคัญและ feature เสริมควรถูกจัดลำดับก่อนเกิด incident
30SLO และ Error Budget
Service Level Indicator คือสิ่งที่วัด เช่นสัดส่วน request สำเร็จภายใน 300 ms SLO คือเป้าหมาย เช่น 99.9% ต่อ 30 วัน Error budget คือส่วนที่ยอมพลาดได้ ใช้ตัดสินสมดุลระหว่างการเปลี่ยนเร็วกับ reliability
Availability อย่างเดียวไม่พอ หาก response ถูกแต่ช้าหลังผู้ใช้เลิก SLO ควรรวม latency และ correctness ใน critical user journey ไม่ใช้ CPU utilization เป็นตัวแทนประสบการณ์ผู้ใช้
31สรุปและขั้นตอนถัดไป
ระบบขนาดใหญ่ไม่ได้เกิดจากการวาง load balancer หน้า server หลายตัวเพียงอย่างเดียว ต้องจัด state, cache, queue, partition, timeout, retry, deployment และ observability ให้ทำงานเป็นระบบเดียว Scale out ช่วยเพิ่ม capacity แต่เปลี่ยน local call เป็น network interaction และเพิ่ม failure modes
บทถัดไปจะมอง infrastructure ที่ทำให้การจัดสรรเครื่องและ isolation ยืดหยุ่นขึ้น ตั้งแต่ virtual machine ไปสู่ container, orchestration และ serverless พร้อมคำถามสำคัญว่า elasticity ที่ดูเหมือนเพิ่มเครื่องได้ทันทีนั้นมี startup, placement, cost และ shared-resource interference ซ่อนอยู่ตรงไหนครับ