Redis เกินกว่าแค่ Cache: Data Structures, Pub/Sub และแอปพลิเคชันแบบ Real-Time
เรียนรู้วิธีใช้ Redis มากกว่าแค่ cache — สำรวจ data structures, pub/sub messaging, streams และรูปแบบการสร้างแอปพลิเคชันที่รวดเร็วและรองรับการขยายตัวได้
นักพัฒนาส่วนใหญ่รู้จัก Redis ในฐานะ "cache ที่รวดเร็ว" แต่ Redis คือเซิร์ฟเวอร์ข้อมูลแบบ in-memory ที่มีฟีเจอร์ครบครัน รองรับทั้ง session management, leaderboard แบบ real-time, rate limiting, message queues และอีกมากมาย — ทั้งหมดนี้ด้วย latency ระดับต่ำกว่ามิลลิวินาที
ทำไม Redis ถึงเร็วมาก
Redis เก็บข้อมูลทุกอย่างไว้ใน memory และใช้ single-threaded event loop ในการประมวลผลคำสั่ง ไม่มี disk I/O ในการอ่านข้อมูล ไม่มีการรอ lock และไม่มี context switching ผลลัพธ์คือ: 100,000+ operations ต่อวินาที บนฮาร์ดแวร์ทั่วไป
ตัวเปรียบเทียบ latency ทั่วไป:
┌─────────────────┬──────────────┐
│ Operation │ Latency │
├─────────────────┼──────────────┤
│ Redis GET │ 0.1 ms │
│ PostgreSQL SEL │ 1-5 ms │
│ REST API call │ 50-200 ms │
│ Disk read │ 5-10 ms │
└─────────────────┴──────────────┘
Data structures หลัก
Redis ไม่ได้เป็นแค่ key-value แต่รองรับ data structures ที่หลากหลายซึ่งตอบโจทย์ความต้องการของแอปพลิเคชันได้โดยตรง
Strings — พื้นฐานที่สำคัญ
Strings เก็บข้อความ ตัวเลข หรือข้อมูลไบนารีได้สูงสุด 512 MB:
SET user:1001:name "Alice"
GET user:1001:name # "Alice"
# Atomic counter
INCR page:views # 1, 2, 3, ...
INCRBY cart:total 2500 # Add 25.00 (store cents)
# Expiring keys (TTL)
SET session:abc123 "{...}" EX 3600 # Expires in 1 hour
TTL session:abc123 # Seconds remaining
Hashes — objects ขนาดเล็ก
Hashes เหมาะมากสำหรับการเก็บ objects โดยไม่ต้องมีค่าใช้จ่ายในการ serialize:
HSET user:1001 name "Alice" email "alice@example.com" plan "pro"
HGET user:1001 name # "Alice"
HGETALL user:1001 # All fields and values
HINCRBY user:1001 logins 1 # Atomic field increment
Hashes ใช้ memory น้อยกว่าถึง 10 เท่า เมื่อเทียบกับการเก็บแต่ละ field เป็น key แยกกัน
Lists — queues และ feeds
Collection แบบเรียงลำดับที่รองรับการ push/pop จากทั้งสองด้าน:
LPUSH notifications:alice "New comment on your post"
LPUSH notifications:alice "You have a new follower"
LRANGE notifications:alice 0 9 # Latest 10 notifications
# Use as a queue (producer/consumer)
RPUSH queue:emails "send-welcome"
LPOP queue:emails # Process next job
Sets — collections ที่ไม่ซ้ำกัน
Collection ที่ไม่เรียงลำดับของ strings ที่ไม่ซ้ำกัน:
SADD tags:post:42 "redis" "database" "nosql"
SMEMBERS tags:post:42 # All tags
SISMEMBER tags:post:42 "redis" # true
# Set operations
SINTER tags:post:42 tags:post:99 # Common tags
SUNION tags:post:42 tags:post:99 # All tags combined
Sorted Sets — leaderboards และการจัดอันดับ
Sets ที่มี score สำหรับแต่ละ member และเรียงลำดับโดยอัตโนมัติ:
ZADD leaderboard 1500 "alice" 2300 "bob" 1800 "charlie"
ZREVRANGE leaderboard 0 2 WITHSCORES # Top 3 players
ZRANK leaderboard "alice" # Rank (0-based)
ZINCRBY leaderboard 100 "alice" # Alice scores 100 points
นี่คือวิธีที่ leaderboard ในเกม, โพสต์ยอดนิยม และ priority queues ถูกสร้างในระดับ scale ใหญ่
รูปแบบการใช้งานในโลกจริง
Pattern 1: Session storage
แทนที่จะเก็บ session ไว้ในฐานข้อมูล ใช้ Redis เพื่อการค้นหาที่รวดเร็วทันที:
import redis
r = redis.Redis()
# Store session
r.setex(f"session:{token}", 3600, json.dumps(user_data))
# Retrieve session
data = r.get(f"session:{token}")
ทำไม? Session ในฐานข้อมูลเพิ่ม latency 1-5ms ต่อ request แต่ Redis session เพิ่มเพียง 0.1ms ที่ 1,000 req/s ประหยัดเวลาได้ถึง 5 วินาทีต่อวินาที
Pattern 2: Rate limiting
Sliding window rate limiter ด้วยเพียง 3 คำสั่ง:
# Allow 100 requests per minute per user
MULTI
INCR rate:user:1001
EXPIRE rate:user:1001 60
EXEC
# Check: if INCR result > 100, reject request
Pattern 3: Caching แบบ cache-aside
รูปแบบ caching ที่พบบ่อยที่สุด:
def get_user(user_id):
# 1. Check cache
cached = redis.get(f"user:{user_id}")
if cached:
return json.loads(cached)
# 2. Cache miss → query database
user = db.query("SELECT * FROM users WHERE id = %s", user_id)
# 3. Populate cache (expire in 5 minutes)
redis.setex(f"user:{user_id}", 300, json.dumps(user))
return user
Pattern 4: Pub/Sub สำหรับ real-time events
Redis Pub/Sub เปิดใช้งาน real-time messaging ระหว่าง services:
# Subscriber (listens for messages)
SUBSCRIBE chat:room:42
# Publisher (sends messages)
PUBLISH chat:room:42 "Hello everyone!"
ในโค้ดแอปพลิเคชัน:
# Publisher
redis.publish("notifications", json.dumps({
"type": "new_order",
"order_id": 12345
}))
# Subscriber
pubsub = redis.pubsub()
pubsub.subscribe("notifications")
for message in pubsub.listen():
handle_notification(message)
Pattern 5: Distributed locks
ป้องกัน race conditions ระหว่างเซิร์ฟเวอร์หลายตัว:
# Acquire lock (NX = only if not exists, EX = expire)
SET lock:invoice:gen "worker-1" NX EX 30
# Release lock (only if you own it — use Lua script)
Redis Streams — event sourcing และ message queues
Streams คือคำตอบของ Redis สำหรับ event logs แบบ Kafka:
# Add events
XADD orders * user_id 1001 product "Widget" qty 3
XADD orders * user_id 1002 product "Gadget" qty 1
# Read latest events
XRANGE orders - + COUNT 10
# Consumer groups (multiple workers)
XGROUP CREATE orders processors $ MKSTREAM
XREADGROUP GROUP processors worker-1 COUNT 5 BLOCK 2000 STREAMS orders >
Streams มอบ event logs ที่คงทนและสามารถเล่นซ้ำได้พร้อม consumer groups — เหมาะมากสำหรับการสื่อสารระหว่าง microservices
เคล็ดลับด้านประสิทธิภาพ
1. ใช้ pipelining สำหรับการทำงานจำนวนมาก
แทนที่จะส่ง 100 round trips แยกกัน ส่ง 100 คำสั่งพร้อมกันครั้งเดียว:
pipe = redis.pipeline()
for i in range(100):
pipe.set(f"key:{i}", f"value:{i}")
pipe.execute() # Single round trip
2. เลือก data structure ให้เหมาะสม
- ต้องการ counter? →
INCR(ไม่ใช่ GET + SET) - ต้องการ object? → Hash (ไม่ใช่ JSON string ที่ serialize)
- ต้องการ items ที่ไม่ซ้ำ? → Set (ไม่ใช่ List + logic ตรวจซ้ำ)
- ต้องการจัดอันดับ? → Sorted Set (ไม่ใช่ Sort หลัง query)
3. ตั้ง TTL ทุกครั้ง
Memory มีจำกัด ตั้ง expiration สำหรับ cache keys เสมอ:
SET cache:api:result "{...}" EX 300 # 5-minute TTL
4. ใช้รูปแบบการตั้งชื่อ key ที่สม่ำเสมอ
resource:id:field
user:1001:profile
cache:api:v2:products
session:abc123
queue:emails:pending
Persistence: RDB vs AOF
Redis สามารถบันทึกข้อมูลลง disk เพื่อความทนทาน:
| Mode | วิธีทำงาน | ข้อดีข้อเสีย |
|---|---|---|
| RDB | Point-in-time snapshots | Recovery เร็ว แต่อาจสูญเสียข้อมูลได้ |
| AOF | บันทึกทุก write operation | ช้ากว่า แต่สูญเสียข้อมูลน้อยมาก |
| Both | RDB + AOF รวมกัน | ความทนทานสูงสุด |
# In redis.conf
save 900 1 # Snapshot if 1 key changed in 900s
appendonly yes # Enable AOF
appendfsync everysec # Fsync every second
อ้างอิงคำสั่งด่วน
ต้องการค้นหาคำสั่ง Redis เฉพาะเจาะจง? ใช้ Redis Cheat Sheet ของเรา — ครอบคลุมคำสั่งกว่า 200+ คำสั่ง จัดหมวดหมู่ตาม data structure พร้อม syntax, ตัวอย่าง และคัดลอกด้วยคลิกเดียว
สรุป
Redis มีความสามารถมากกว่าแค่ cache data structures ของมันสอดคล้องกับความต้องการของแอปพลิเคชันจริงได้อย่างลงตัว:
- Strings → sessions, counters, cache ง่ายๆ
- Hashes → user profiles, configuration objects
- Lists → queues, activity feeds, รายการล่าสุด
- Sets → tags, unique visitors, ความสัมพันธ์
- Sorted Sets → leaderboards, rankings, priority queues
- Streams → event sourcing, message queues
- Pub/Sub → real-time notifications, chat
เริ่มต้นด้วย caching แต่อย่าหยุดแค่นั้น Redis สามารถแทนที่ส่วนประกอบของ infrastructure หลายอย่างใน stack ของคุณ — และทำทั้งหมดนั้นด้วยความเร็วระดับต่ำกว่ามิลลิวินาที