Home > Blog > tech

System Design คืออะไร? สอนออกแบบระบบ สำหรับ Senior Developer และสัมภาษณ์งาน 2026

system design architecture guide
System Design Architecture Guide 2026
2026-04-08 | tech | 3600 words

System Design เป็นทักษะที่สำคัญที่สุดสำหรับ Senior Developer และ Software Architect ไม่ว่าจะเป็นการออกแบบระบบจริงในที่ทำงานหรือการสัมภาษณ์งานในบริษัท Tech ชั้นนำ คุณจะต้องเข้าใจวิธีการออกแบบระบบที่รองรับผู้ใช้หลายล้านคน มีความเสถียร ปลอดภัย และขยายตัวได้

บทความนี้จะสอน System Design ตั้งแต่แนวคิดพื้นฐานจนถึงการออกแบบระบบจริง ครอบคลุม Scalability, Load Balancing, Caching, Database Design, CAP Theorem, Message Queues และตัวอย่างการออกแบบระบบยอดนิยม เช่น URL Shortener, Chat System และ Newsfeed

ทำไม System Design ถึงสำคัญ?

ในยุคที่แอปพลิเคชันต้องรองรับผู้ใช้หลายล้านคนพร้อมกัน การเขียนโค้ดที่ทำงานได้อย่างเดียวไม่เพียงพอ คุณต้องคิดเรื่อง Performance ว่าระบบตอบสนองเร็วแค่ไหน Reliability ว่าระบบพังไหมเมื่อมีปัญหา Scalability ว่าระบบรองรับ Traffic ที่เพิ่มขึ้นได้หรือไม่ และ Maintainability ว่าระบบดูแลรักษาง่ายไหมเมื่อทีมโตขึ้น

ในการสัมภาษณ์งานตำแหน่ง Senior Developer ที่บริษัท Tech ใหญ่ๆ เช่น Google, Meta, Amazon, LINE, Agoda รอบ System Design มักจะเป็นรอบที่ตัดสินว่าคุณจะได้ตำแหน่ง Senior หรือไม่ เพราะมันแสดงให้เห็นว่าคุณมองปัญหาในภาพรวม ไม่ใช่แค่เขียนโค้ดฟังก์ชันเดียว

Scalability — การขยายตัวของระบบ

Scalability คือความสามารถของระบบในการรองรับ Load ที่เพิ่มขึ้น ไม่ว่าจะเป็นจำนวนผู้ใช้ จำนวน Request หรือปริมาณข้อมูลที่เพิ่มขึ้น มีสองแนวทางหลักในการ Scale ระบบ

Vertical Scaling (Scale Up)

เพิ่มพลังให้เครื่องเดิม เช่น เพิ่ม CPU, RAM, SSD ข้อดีคือง่าย ไม่ต้องเปลี่ยนโค้ด แต่มีข้อจำกัดคือมีเพดานสูงสุดที่เครื่องเดียวจะรับได้ และถ้าเครื่องพังก็คือทั้งระบบล่ม (Single Point of Failure)

Horizontal Scaling (Scale Out)

เพิ่มจำนวนเครื่องแทน โดยกระจาย Load ไปยังหลายเครื่อง ข้อดีคือไม่มีเพดาน เพิ่มเครื่องได้เรื่อยๆ และถ้าเครื่องหนึ่งพังก็เครื่องอื่นรับแทนได้ แต่ต้องออกแบบให้ Application เป็น Stateless และจัดการเรื่อง Data Consistency ให้ดี

หัวข้อVertical ScalingHorizontal Scaling
วิธีการเพิ่ม CPU/RAM เครื่องเดิมเพิ่มจำนวนเครื่อง
ความซับซ้อนต่ำสูง
ค่าใช้จ่ายเพิ่มแบบ Exponentialเพิ่มแบบ Linear
เพดานจำกัด (Hardware Limit)ไม่จำกัด (เพิ่มเครื่องได้เรื่อยๆ)
AvailabilitySPOF (Single Point of Failure)High Availability (HA)
ตัวอย่างAWS RDS Scale Upเพิ่ม EC2 ใน Auto Scaling Group
กฎทั่วไป: เริ่มด้วย Vertical Scaling เพราะง่ายและเร็ว เมื่อถึงขีดจำกัดหรือต้องการ High Availability ค่อยย้ายไป Horizontal Scaling ไม่ต้อง Over-engineer ตั้งแต่วันแรก

Load Balancing — กระจาย Traffic

Load Balancer เป็นตัวกลางที่รับ Request จากผู้ใช้และกระจายไปยัง Server หลายตัว ทำให้ไม่มี Server ตัวไหนรับ Load หนักเกินไป และยังช่วยเรื่อง High Availability เมื่อ Server ตัวใดตัวหนึ่งพัง Load Balancer จะหยุดส่ง Traffic ไปที่ Server นั้นอัตโนมัติ

อัลกอริทึมการกระจาย Load

Algorithmวิธีการเหมาะกับ
Round Robinวนส่งทีละ Server ตามลำดับServer ที่มี Spec เท่ากัน
Weighted Round RobinServer แรงกว่าได้ส่วนแบ่งมากกว่าServer ที่มี Spec ต่างกัน
Least Connectionsส่งไป Server ที่มี Connection น้อยสุดRequest ที่ใช้เวลาต่างกัน
IP HashHash IP ของผู้ใช้เพื่อส่งไป Server เดิมเสมอSession Sticky
Least Response Timeส่งไป Server ที่ตอบเร็วที่สุดต้องการ Performance สูงสุด

ระดับของ Load Balancing

# Nginx Load Balancer Configuration
upstream backend {
    # Weighted Round Robin
    server app1.internal:8080 weight=3;
    server app2.internal:8080 weight=2;
    server app3.internal:8080 weight=1;

    # Health Check
    # Nginx จะหยุดส่ง Traffic ไปที่ Server ที่ fail
    # max_fails=3 ลองผิด 3 ครั้ง
    # fail_timeout=30s หยุดส่ง 30 วินาทีก่อนลองใหม่
    server app1.internal:8080 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;

    location / {
        proxy_pass http://backend;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;

        # Connection pooling
        proxy_http_version 1.1;
        proxy_set_header Connection "";

        # Timeout settings
        proxy_connect_timeout 5s;
        proxy_read_timeout 30s;
    }

    # Route API ไป API servers
    location /api/ {
        proxy_pass http://api_backend;
    }

    # Route static files ไป CDN/Static servers
    location /static/ {
        proxy_pass http://static_backend;
        proxy_cache_valid 200 1d;
    }
}

Caching — เร่งความเร็วระบบ

Caching เป็นเทคนิคที่ทรงพลังที่สุดในการเพิ่ม Performance ของระบบ หลักการคือเก็บผลลัพธ์ที่คำนวณหรือ Query มาแล้วไว้ในหน่วยความจำที่เข้าถึงได้เร็ว เมื่อมี Request เดิมเข้ามาก็ส่งผลลัพธ์จาก Cache ไปเลยโดยไม่ต้องคำนวณใหม่ ช่วยลด Latency และลด Load บน Database ได้อย่างมาก

ระดับของ Cache

ระดับตำแหน่งตัวอย่างLatency
Browser Cacheเบราว์เซอร์ผู้ใช้Cache-Control Headers0ms
CDN CacheEdge Server ทั่วโลกCloudFlare, AWS CloudFront10-50ms
Application CacheApplication ServerRedis, Memcached1-5ms
Database CacheDatabase ServerQuery Cache, Buffer Pool5-10ms

Cache Strategies

# Cache-Aside (Lazy Loading) — กลยุทธ์ที่นิยมมากที่สุด
import redis
import json

cache = redis.Redis(host='localhost', port=6379, db=0)

def get_user(user_id):
    # 1. ลองดึงจาก Cache ก่อน
    cache_key = f"user:{user_id}"
    cached = cache.get(cache_key)
    if cached:
        return json.loads(cached)  # Cache Hit!

    # 2. ถ้าไม่มีใน Cache ดึงจาก Database
    user = db.query("SELECT * FROM users WHERE id = %s", user_id)

    # 3. เก็บผลลัพธ์ลง Cache (TTL 1 ชั่วโมง)
    cache.setex(cache_key, 3600, json.dumps(user))

    return user  # Cache Miss

def update_user(user_id, data):
    # อัปเดต Database
    db.execute("UPDATE users SET ... WHERE id = %s", user_id)
    # ลบ Cache เก่าออก (Invalidation)
    cache.delete(f"user:{user_id}")


# Write-Through — เขียน Cache และ Database พร้อมกัน
def save_user_write_through(user_id, data):
    # เขียนทั้ง Cache และ DB
    cache.setex(f"user:{user_id}", 3600, json.dumps(data))
    db.execute("UPDATE users SET ... WHERE id = %s", user_id)
    # ข้อดี: Cache กับ DB ตรงกันเสมอ
    # ข้อเสีย: Write ช้าลง (ต้องเขียน 2 ที่)


# Write-Behind (Write-Back) — เขียน Cache ก่อน แล้ว Async เขียน DB ทีหลัง
def save_user_write_behind(user_id, data):
    # เขียน Cache ทันที
    cache.setex(f"user:{user_id}", 3600, json.dumps(data))
    # ส่งไป Background Queue เพื่อเขียน DB
    queue.enqueue('write_db', user_id=user_id, data=data)
    # ข้อดี: Write เร็วมาก
    # ข้อเสีย: ถ้า Cache พังก่อน DB เขียน ข้อมูลหาย

Cache Invalidation — ปัญหาที่ยากที่สุด

มีคำกล่าวว่า "There are only two hard things in Computer Science: cache invalidation and naming things" การตัดสินใจว่าเมื่อไหร่ควรลบ Cache และวิธีจัดการเป็นเรื่องที่ต้องคิดให้รอบคอบ

Database Design — การออกแบบฐานข้อมูล

SQL vs NoSQL

หัวข้อSQL (Relational)NoSQL
โครงสร้างSchema ตายตัว (Table, Row, Column)ยืดหยุ่น (Document, Key-Value, Graph)
ความสัมพันธ์JOIN ได้ง่ายต้อง Denormalize หรือ Query หลายครั้ง
ACIDรองรับเต็มรูปแบบบางตัวรองรับบางส่วน (BASE)
ScalingVertical เป็นหลัก (Read Replica สำหรับ Read)Horizontal Scaling ง่าย (Sharding)
เหมาะกับข้อมูลที่มีความสัมพันธ์ซับซ้อน ต้องการ Consistencyข้อมูลมหาศาล ต้องการ Flexibility และ Speed
ตัวอย่างPostgreSQL, MySQLMongoDB, DynamoDB, Cassandra, Redis

Database Replication

Replication คือการทำสำเนาข้อมูลไปยังหลาย Server เพื่อเพิ่ม Availability และ Read Performance รูปแบบที่พบบ่อยที่สุดคือ Master-Slave (Primary-Replica) โดย Master รับ Write ทั้งหมด แล้ว Replicate ข้อมูลไปยัง Slave หลายตัว Slave รับเฉพาะ Read

# Database Replication Architecture
#
#   [Client] --Write--> [Master DB]
#                            |
#                    Replication (Async/Semi-sync)
#                    /        |         \
#              [Slave 1]  [Slave 2]  [Slave 3]
#                  ^          ^          ^
#                  |          |          |
#               [Client Read Requests via Load Balancer]

# Python — Read/Write Splitting
class DatabaseRouter:
    def __init__(self):
        self.master = create_connection("master-db.internal:5432")
        self.replicas = [
            create_connection("replica1-db.internal:5432"),
            create_connection("replica2-db.internal:5432"),
            create_connection("replica3-db.internal:5432"),
        ]
        self._replica_index = 0

    def get_write_connection(self):
        return self.master

    def get_read_connection(self):
        # Round Robin เลือก Replica
        conn = self.replicas[self._replica_index]
        self._replica_index = (self._replica_index + 1) % len(self.replicas)
        return conn

    def execute_write(self, query, params=None):
        conn = self.get_write_connection()
        return conn.execute(query, params)

    def execute_read(self, query, params=None):
        conn = self.get_read_connection()
        return conn.execute(query, params)

Database Sharding

Sharding คือการแบ่งข้อมูลออกเป็นส่วนๆ (Shards) แล้วเก็บไว้คนละ Server ใช้เมื่อข้อมูลมีขนาดใหญ่มากจนเครื่องเดียวรับไม่ไหว เป็นเทคนิค Horizontal Scaling ของ Database

# Sharding Strategies:
#
# 1. Range-based Sharding — แบ่งตามช่วง
#    User ID 1-1M     -> Shard 1
#    User ID 1M-2M    -> Shard 2
#    ข้อเสีย: Hotspot (Shard ใหม่จะโดน Load มากกว่า)
#
# 2. Hash-based Sharding — Hash แล้วแบ่ง
#    shard = hash(user_id) % num_shards
#    ข้อดี: กระจายสม่ำเสมอ
#    ข้อเสีย: เพิ่ม Shard ยาก (ต้อง Resharding)
#
# 3. Directory-based Sharding — Lookup Table
#    เก็บ Mapping ว่า Key ไหนอยู่ Shard ไหน
#    ข้อดี: ยืดหยุ่นสูง
#    ข้อเสีย: Lookup Table เป็น SPOF

# Consistent Hashing — แก้ปัญหา Resharding
import hashlib

class ConsistentHash:
    def __init__(self, nodes, virtual_nodes=150):
        self.ring = {}
        self.sorted_keys = []

        for node in nodes:
            for i in range(virtual_nodes):
                key = self._hash(f"{node}:{i}")
                self.ring[key] = node
                self.sorted_keys.append(key)

        self.sorted_keys.sort()

    def _hash(self, key):
        return int(hashlib.md5(key.encode()).hexdigest(), 16)

    def get_node(self, data_key):
        if not self.ring:
            return None
        h = self._hash(str(data_key))
        for key in self.sorted_keys:
            if h <= key:
                return self.ring[key]
        return self.ring[self.sorted_keys[0]]

# ใช้งาน
shards = ConsistentHash(["shard1", "shard2", "shard3"])
target = shards.get_node("user:12345")  # -> "shard2"

CAP Theorem

CAP Theorem ระบุว่าระบบ Distributed Database ไม่สามารถมีทั้ง 3 อย่างพร้อมกันได้ ต้องเลือกเพียง 2 จาก 3

ในความเป็นจริง Network Partition เกิดขึ้นได้เสมอ ดังนั้น P เป็นสิ่งที่ต้องมี เหลือให้เลือกระหว่าง C กับ A:

เลือกได้เสียตัวอย่าง
CPConsistency + Partition Toleranceระบบอาจ Unavailable ชั่วขณะMongoDB (default), HBase, Redis Cluster
APAvailability + Partition Toleranceข้อมูลอาจไม่ตรงกันชั่วขณะCassandra, DynamoDB, CouchDB
ในทางปฏิบัติ: ระบบธนาคารหรือการเงินมักเลือก CP (Consistency สำคัญกว่า) ส่วนระบบ Social Media หรือ CDN มักเลือก AP (ให้ผู้ใช้เข้าถึงได้เสมอสำคัญกว่า ข้อมูลจะ Sync ทีหลังก็ได้)

Consistency Patterns

Message Queues — การสื่อสารแบบ Asynchronous

Message Queue เป็นตัวกลางที่ช่วยให้ Service ต่างๆ สื่อสารกันแบบ Asynchronous ช่วยลด Coupling ระหว่าง Service เพิ่มความเสถียร และรองรับ Spike Traffic ได้ดีขึ้น

# ตัวอย่างระบบ Order Processing ด้วย Message Queue
#
# [User] -> [Order Service] -> [Message Queue] -> [Payment Service]
#                                    |          -> [Inventory Service]
#                                    |          -> [Notification Service]
#                                    |          -> [Analytics Service]
#
# ข้อดี:
# 1. ถ้า Payment Service พังชั่วคราว Message ไม่หาย (Durability)
# 2. Order Service ไม่ต้องรอ Payment Service ทำเสร็จ (Async)
# 3. เพิ่ม Consumer ได้ตามต้องการ (Scalability)
# 4. Service ไม่ต้องรู้จักกัน (Decoupling)

# Python — Producer (ส่ง Message)
import pika
import json

connection = pika.BlockingConnection(pika.ConnectionParameters('rabbitmq'))
channel = connection.channel()
channel.queue_declare(queue='orders', durable=True)

def publish_order(order):
    channel.basic_publish(
        exchange='',
        routing_key='orders',
        body=json.dumps(order),
        properties=pika.BasicProperties(
            delivery_mode=2,  # Message จะถูกเก็บบน Disk (Persistent)
            content_type='application/json'
        )
    )

# Python — Consumer (รับ Message)
def process_order(ch, method, properties, body):
    order = json.loads(body)
    try:
        process_payment(order)
        update_inventory(order)
        send_notification(order)
        ch.basic_ack(delivery_tag=method.delivery_tag)  # ยืนยันว่าทำเสร็จแล้ว
    except Exception as e:
        ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)  # ส่งกลับ Queue

channel.basic_qos(prefetch_count=1)  # รับทีละ 1 Message
channel.basic_consume(queue='orders', on_message_callback=process_order)

Microservices vs Monolith

Monolithic Architecture

ทุกอย่างอยู่ใน Application เดียว ง่ายต่อการพัฒนาและ Deploy ในช่วงแรก แต่เมื่อ Codebase โตขึ้น จะเริ่มยากต่อการดูแลรักษา ทดสอบ และ Deploy เพราะการเปลี่ยนแปลงเล็กน้อยอาจกระทบทั้งระบบ การ Scale ต้อง Scale ทั้ง Application แม้มีแค่ส่วนเดียวที่โดน Load มาก

Microservices Architecture

แยกระบบออกเป็น Service เล็กๆ หลายตัว แต่ละตัวทำหน้าที่เฉพาะ สื่อสารกันผ่าน API หรือ Message Queue ข้อดีคือ Deploy แต่ละ Service ได้อิสระ Scale เฉพาะ Service ที่ต้องการ ใช้ Technology Stack ที่เหมาะสมกับแต่ละ Service ได้ แต่ข้อเสียคือเพิ่มความซับซ้อนของระบบอย่างมาก ต้องจัดการเรื่อง Service Discovery, Distributed Tracing, Data Consistency ข้าม Service

หัวข้อMonolithMicroservices
DeploymentDeploy ทั้งก้อนDeploy แยก Service
ScalingScale ทั้ง AppScale เฉพาะ Service
Developmentง่ายตอนแรก ยากตอนหลังยากตอนแรก ง่ายตอนหลัง
Debuggingง่าย (ทุกอย่างอยู่ที่เดียว)ยาก (Distributed Tracing)
Tech Stackเดียวกันทั้งหมดเลือกได้ตาม Service
Teamทีมเดียวดูแลทั้งหมดแต่ละทีมดูแลแต่ละ Service
ข้อควรระวัง: อย่าเริ่มด้วย Microservices ถ้าทีมเล็ก (น้อยกว่า 10 คน) หรือ Product ยังไม่ชัดเจน เริ่มด้วย Monolith ที่มีโครงสร้างดี (Modular Monolith) แล้วค่อยแยกเป็น Microservices เมื่อเจอปัญหาจริง

Rate Limiting — ควบคุมปริมาณ Request

Rate Limiting เป็นเทคนิคป้องกันไม่ให้ผู้ใช้หรือ Client ส่ง Request มากเกินไป ช่วยป้องกัน DDoS Attack ป้องกัน API Abuse และทำให้ระบบมีเสถียรภาพ

# Token Bucket Algorithm — อัลกอริทึม Rate Limiting ที่นิยมที่สุด
import time
import redis

class TokenBucket:
    def __init__(self, redis_client, key, max_tokens, refill_rate):
        self.redis = redis_client
        self.key = key
        self.max_tokens = max_tokens      # จำนวน Token สูงสุดในถัง
        self.refill_rate = refill_rate    # Token ที่เติมต่อวินาที

    def allow_request(self):
        pipe = self.redis.pipeline()
        now = time.time()

        # ดึงข้อมูลปัจจุบัน
        data = self.redis.hgetall(self.key)
        tokens = float(data.get('tokens', self.max_tokens))
        last_refill = float(data.get('last_refill', now))

        # เติม Token ตามเวลาที่ผ่านไป
        elapsed = now - last_refill
        tokens = min(self.max_tokens, tokens + elapsed * self.refill_rate)

        if tokens >= 1:
            # อนุญาต — ใช้ Token 1 อัน
            tokens -= 1
            pipe.hset(self.key, mapping={'tokens': tokens, 'last_refill': now})
            pipe.expire(self.key, 3600)
            pipe.execute()
            return True
        else:
            # ปฏิเสธ — Token หมด
            pipe.hset(self.key, mapping={'tokens': tokens, 'last_refill': now})
            pipe.execute()
            return False

# ใช้งาน: 100 requests / minute per user
limiter = TokenBucket(redis_client, f"rate:{user_id}", max_tokens=100, refill_rate=100/60)

ตัวอย่างการออกแบบระบบ (System Design Examples)

ออกแบบ URL Shortener (เช่น bit.ly)

# Requirements:
# - ผู้ใช้ส่ง Long URL -> ระบบสร้าง Short URL (7 ตัวอักษร)
# - เมื่อเข้า Short URL -> Redirect ไป Long URL
# - รองรับ 100M URLs, 10K writes/sec, 100K reads/sec

# Architecture:
# [Client] -> [Load Balancer] -> [API Servers (Stateless)]
#                                      |
#                              [Redis Cache] + [PostgreSQL/Cassandra]
#
# Key Decisions:
# 1. Short URL Generation: Base62 encoding ของ Auto-increment ID
#    หรือ MD5 Hash ตัดเอา 7 ตัวแรก (ต้องเช็ค Collision)
# 2. Database: NoSQL (Cassandra) สำหรับ Scale, SQL สำหรับเริ่มต้น
# 3. Cache: เก็บ Short->Long mapping ใน Redis (Cache-Aside)
# 4. Analytics: แยก Service สำหรับนับ Click (Async via Queue)

import hashlib
import string

BASE62 = string.ascii_letters + string.digits  # a-z A-Z 0-9

def encode_base62(num):
    if num == 0:
        return BASE62[0]
    result = []
    while num:
        result.append(BASE62[num % 62])
        num //= 62
    return ''.join(reversed(result))

def generate_short_url(long_url, counter):
    # วิธี 1: Base62 ของ Counter (ไม่มี Collision)
    return encode_base62(counter)

    # วิธี 2: Hash-based (ต้องเช็ค Collision)
    # h = hashlib.md5(long_url.encode()).hexdigest()
    # return encode_base62(int(h[:10], 16))[:7]

ออกแบบ Chat System (เช่น LINE, WhatsApp)

# Requirements:
# - 1-on-1 Chat และ Group Chat
# - รองรับ 50M DAU, 40 messages/user/day
# - ส่ง/รับข้อความ Real-time
# - เก็บประวัติ Chat, Read Receipt, Online Status

# Architecture:
# [Mobile/Web Client] <--WebSocket--> [Chat Servers (Stateful)]
#                                          |
#                                    [Message Queue]
#                                     /          \
#                              [Message Store]  [Push Notification]
#                              (Cassandra)       (FCM/APNs)
#
# Key Decisions:
# 1. Protocol: WebSocket สำหรับ Real-time bidirectional
# 2. Message Store: Cassandra (Partition by chat_id, sort by timestamp)
# 3. Service Discovery: ใช้ Redis เก็บ User -> Chat Server mapping
# 4. Group Chat: Fan-out on write (เขียน message ไปทุก member's inbox)
#    หรือ Fan-out on read (member ดึง message จาก group inbox)

# Message Schema (Cassandra)
# CREATE TABLE messages (
#   chat_id UUID,
#   message_id TIMEUUID,
#   sender_id UUID,
#   content TEXT,
#   type TEXT,  -- 'text', 'image', 'video'
#   created_at TIMESTAMP,
#   PRIMARY KEY (chat_id, message_id)
# ) WITH CLUSTERING ORDER BY (message_id DESC);

ออกแบบ Newsfeed (เช่น Facebook, Twitter)

# Requirements:
# - ผู้ใช้เห็น Post จากเพื่อนและ Pages ที่ Follow
# - เรียงตาม Relevance + Time
# - รองรับ 300M DAU

# Architecture:
# [Client] -> [Load Balancer] -> [Feed Service]
#                                     |
#                    [Feed Cache (Redis)] <- [Feed Generator]
#                                                  |
#                        [Post Service] + [Social Graph] + [Ranking Service]
#
# Two Approaches:
#
# 1. Fan-out on Write (Push Model) — เมื่อ User A โพสต์
#    เขียน Post ไปทุก Follower's Feed Cache ทันที
#    ข้อดี: อ่าน Feed เร็วมาก (อ่านจาก Cache)
#    ข้อเสีย: Write แพงมาก ถ้ามี Follower ล้านคน = เขียนล้านครั้ง
#    เหมาะกับ: User ที่มี Follower ไม่มาก
#
# 2. Fan-out on Read (Pull Model) — เมื่ออ่าน Feed
#    ดึง Post ล่าสุดจากทุกคนที่ Follow แล้ว Merge + Rank
#    ข้อดี: Write ง่าย (เขียนที่เดียว)
#    ข้อเสีย: อ่าน Feed ช้า (ต้อง Query หลาย Source)
#    เหมาะกับ: Celebrity/Pages ที่มี Follower เยอะมาก
#
# Hybrid Approach (ที่ Facebook/Twitter ใช้):
#    - User ทั่วไป (Follower < 10K): Fan-out on Write
#    - Celebrity (Follower > 10K): Fan-out on Read
#    - Merge ทั้งสอง Source เมื่อสร้าง Feed

Back-of-Envelope Estimation — การประมาณอย่างรวดเร็ว

ในการสัมภาษณ์ System Design คุณต้องประมาณตัวเลขอย่างรวดเร็ว นี่คือตัวเลขที่ควรจำ

ResourceLatency / Throughput
L1 Cache Reference0.5 ns
L2 Cache Reference7 ns
Main Memory Reference100 ns
SSD Random Read150 microsec
HDD Seek10 ms
Network Round Trip (Same Datacenter)0.5 ms
Network Round Trip (Cross-continent)150 ms
Read 1 MB from SSD1 ms
Read 1 MB from Network10 ms
Read 1 MB from HDD20 ms
# Quick Math:
# - 1 วันมี 86,400 วินาที (ประมาณ 100K)
# - 1 เดือนมี 2.5 ล้านวินาที
# - QPS (Queries Per Second) = DAU * queries_per_user / 86400
# - Storage = DAU * data_per_user * retention_days
#
# ตัวอย่าง: Twitter-like System
# - 300M DAU, 2 tweets/user/day, 280 bytes/tweet
# - Write QPS = 300M * 2 / 86400 = ~7000 QPS
# - Peak QPS = 7000 * 3 = ~21000 QPS (3x average)
# - Daily Storage = 300M * 2 * 280 bytes = ~168 GB/day
# - Yearly Storage = 168 * 365 = ~60 TB/year (ไม่รวม Media)

RESHADED Framework — สำหรับสัมภาษณ์ System Design

RESHADED เป็น Framework ที่ช่วยให้คุณตอบคำถาม System Design อย่างมีโครงสร้างในการสัมภาษณ์

  1. R — Requirements: ถามให้ชัดเจนว่า Functional Requirements (ระบบทำอะไรได้บ้าง) และ Non-functional Requirements (Performance, Scale, Availability) คืออะไร
  2. E — Estimation: ประมาณ QPS, Storage, Bandwidth, Memory ที่ต้องการ
  3. S — Storage Schema: ออกแบบ Data Model, เลือก Database (SQL vs NoSQL), กำหนด Schema
  4. H — High-level Design: วาด Architecture Diagram แสดง Component หลักๆ และการเชื่อมต่อ
  5. A — API Design: กำหนด API Endpoints, Parameters, Response Format
  6. D — Detailed Design: ลงรายละเอียด Component สำคัญ เช่น Algorithm, Data Flow, Edge Cases
  7. E — Evaluate: ประเมิน Bottlenecks, Single Points of Failure, Trade-offs
  8. D — Distinctive Component: เพิ่ม Component พิเศษที่ทำให้ระบบโดดเด่น เช่น Analytics, ML Ranking
เทคนิคสัมภาษณ์: ในรอบ System Design ที่สำคัญที่สุดคือการถามคำถามเพื่อทำความเข้าใจ Requirements ก่อนลงมือออกแบบ ผู้สัมภาษณ์ต้องการเห็นว่าคุณคิดอย่างเป็นระบบ ไม่ใช่แค่จำ Architecture มาตอบ อย่าลืมพูดถึง Trade-offs ของทุกการตัดสินใจ เพราะไม่มีคำตอบที่ถูกต้องเพียงคำตอบเดียว

Monitoring และ Observability

ระบบที่ออกแบบมาดีแค่ไหน ถ้าไม่มี Monitoring ก็เหมือนขับรถโดยปิดตา ต้องมี 3 Pillars ของ Observability

การตั้ง Alert ที่ดีต้องแจ้งเตือนเฉพาะเรื่องที่ต้องการ Action ไม่ใช่ Alert ทุกเรื่อง Alert Fatigue เป็นปัญหาจริงที่ทำให้ทีมเพิกเฉย Alert สำคัญ

สรุป

System Design เป็นทักษะที่ต้องใช้เวลาฝึกฝนและสะสมประสบการณ์ ไม่มีทางลัด สิ่งสำคัญที่สุดคือการเข้าใจ Trade-offs ของทุกการตัดสินใจ ไม่มี Architecture ที่ดีที่สุด มีแต่ Architecture ที่เหมาะกับบริบทมากที่สุด

สำหรับคนที่เตรียมสัมภาษณ์ ให้ฝึกออกแบบระบบบ่อยๆ ลองออกแบบระบบที่คุณใช้ทุกวัน เช่น Instagram, YouTube, Uber, LINE จะช่วยให้เข้าใจ Pattern ต่างๆ ได้ดี และสำหรับคนที่ทำงานจริง อย่าลืมว่าการ Over-engineer เป็นปัญหาที่พบบ่อยพอๆ กับ Under-engineer เริ่มจากสิ่งที่ง่ายที่สุดที่ตอบโจทย์ แล้วค่อยเพิ่มความซับซ้อนเมื่อเจอปัญหาจริง

หลักการสำคัญที่ควรจำ: KISS (Keep It Simple, Stupid) สำหรับเริ่มต้น แล้วค่อยปรับปรุงเมื่อมีข้อมูลและ Metric บอกว่าจุดไหนเป็น Bottleneck


Back to Blog | iCafe Forex | SiamLanCard | Siam2R