Redis
In-memory data store สำหรับ caching, session management, pub/sub, distributed locking
What I Can Do
- ออกแบบ caching strategy (cache-aside, write-through, write-behind)
- ใช้ Redis เป็น session store สำหรับ stateless services
- Implement pub/sub messaging ระหว่าง services
- ใช้ Redis Streams สำหรับ event sourcing / message queue
- Implement distributed locking ด้วย Redlock algorithm
Commands I Use Daily
# เข้า redis CLI
redis-cli
# basic operations
SET key "value"
GET key
DEL key
# ดู keys ทั้งหมด (ใช้ SCAN ใน production)
KEYS "user:*"
# ดู TTL ที่เหลือ
TTL key
# hash operations
HSET user:1 name "John" age 30
HGETALL user:1
# monitor commands แบบ real-time (debug only)
MONITOR
# server info — memory, clients, stats
INFO memory
INFO clientsKey-Value Store Basics
Redis เก็บข้อมูลเป็น key-value pairs ใน memory — read/write เร็วมาก (sub-millisecond) ทุก key เป็น string, value เป็น data type ต่างๆ ใช้ naming convention แบบ namespace:id:field เช่น user:123:profile
Data Types (String/List/Hash/Set/Sorted Set)
Redis มี data types หลัก 5 แบบ:
- String — ค่าพื้นฐาน, ใช้เก็บ text, number, serialized JSON
- List — ordered collection, push/pop จากหัวหรือท้าย
- Hash — field-value pairs ใน key เดียว เหมาะเก็บ objects
- Set — unordered unique values, รองรับ union/intersection
- Sorted Set — set + score, auto-sorted ใช้ทำ leaderboard, ranking
TTL & Expiration
ทุก key ตั้ง expiration ได้ด้วย EXPIRE key seconds หรือ SET key value EX seconds — key หายอัตโนมัติเมื่อหมดเวลา ใช้สำหรับ cache invalidation, session timeout, rate limiting
Basic Commands
SET/GET/DEL— CRUD พื้นฐานMSET/MGET— batch operations ลด round tripsINCR/DECR— atomic counterEXISTS— check key existenceTYPE— ดู data type ของ key
Hash Operations
Hash เก็บหลาย fields ใน key เดียว — HSET, HGET, HDEL, HGETALL, HINCRBY เหมาะสำหรับเก็บ user profile, session data, object ที่ต้อง update บาง fields โดยไม่ต้อง read/write ทั้ง object
List & Set Operations
- List —
LPUSH/RPUSH(add),LPOP/RPOP(remove),LRANGE(read range),BLPOP(blocking pop สำหรับ queue) - Set —
SADD,SREM,SMEMBERS,SINTER/SUNION(intersection/union),SCARD(count)
Sorted Set
Sorted Set = Set + score — ZADD key score member, ZRANGE (by rank), ZRANGEBYSCORE (by score range), ZRANK ใช้ทำ leaderboard, timeline, priority queue, rate limiter (sliding window)
Pub/Sub
Publisher ส่ง message ไป channel, subscribers ทุกตัวที่ subscribe channel นั้นจะได้รับ — PUBLISH channel msg, SUBSCRIBE channel ไม่มี persistence ถ้า subscriber offline จะพลาด messages ใช้สำหรับ real-time notifications
Pipelining
ส่งหลาย commands พร้อมกันโดยไม่ต้องรอ response ทีละตัว — ลด network round trips จาก N เหลือ 1 ใช้เมื่อต้อง execute batch operations ที่ไม่ depend on each other
Transactions (MULTI/EXEC/WATCH)
MULTI— เริ่ม transaction, commands ถูก queue ไว้EXEC— execute ทุก commands ใน queue แบบ atomicWATCH— optimistic locking, ถ้า watched key เปลี่ยนก่อน EXEC จะ abort transaction
Lua Scripting
Run Lua scripts บน Redis server ด้วย EVAL — atomic execution, ลด round trips, ใช้เมื่อ logic ซับซ้อนเกินกว่า single command เช่น conditional update, complex rate limiting
Persistence (RDB/AOF)
- RDB (Redis Database) — point-in-time snapshot ทุก N วินาที, compact แต่อาจสูญเสียข้อมูลล่าสุด
- AOF (Append Only File) — log ทุก write operation, safer แต่ file ใหญ่กว่า
- ใช้ทั้งคู่ร่วมกันสำหรับ production — AOF สำหรับ durability, RDB สำหรับ backup
Replication (Master-Replica)
Master รับ writes, replicas อ่านจาก master — ใช้ scale read capacity และ high availability ถ้า master ล่ม promote replica เป็น master ใหม่ได้ Replication เป็น asynchronous by default
Cache Patterns (Cache-Aside/Write-Through/Write-Behind)
- Cache-Aside — app อ่านจาก cache ก่อน, ถ้า miss อ่านจาก DB แล้ว write กลับ cache (ใช้บ่อยสุด)
- Write-Through — write ทั้ง cache และ DB พร้อมกัน, consistent แต่ช้ากว่า
- Write-Behind — write cache ก่อน, async write DB ทีหลัง, เร็วแต่เสี่ยง data loss
Eviction Policies
เมื่อ memory เต็ม Redis ต้องลบ keys — policies: noeviction (reject writes), allkeys-lru (ลบ least recently used), volatile-lru (ลบ LRU เฉพาะ keys ที่มี TTL), allkeys-random เลือกตาม use case
Memory Optimization
- ใช้ data types ที่เหมาะสม — Hash สำหรับ small objects แทน multiple keys
OBJECT ENCODINGดู internal encodingMEMORY USAGE keyดู memory ของแต่ละ key- Short key names,
maxmemoryconfig, compression สำหรับ large values
Redis Sentinel
Automatic failover สำหรับ master-replica setup — Sentinel monitors master, ถ้า master ล่มจะ promote replica อัตโนมัติ clients connect ผ่าน Sentinel เพื่อค้นหา current master
Redis Cluster
Horizontal scaling — data ถูก shard ข้าม nodes ด้วย hash slots (16384 slots) แต่ละ node ดูแล subset ของ slots รองรับ automatic failover, add/remove nodes โดยไม่ downtime
Redis Streams
Append-only log data structure — XADD เพิ่ม entries, XREAD อ่าน, consumer groups สำหรับ distributed processing เหมือน Kafka แบบ lightweight ใช้สำหรับ event sourcing, activity feeds, message queue ที่ต้องการ persistence
Distributed Locking (Redlock)
Acquire lock ข้าม multiple Redis instances — algorithm ต้อง acquire lock จาก majority ของ instances (N/2+1) ภายใน TTL ใช้สำหรับ distributed systems ที่ต้องการ mutual exclusion เช่น prevent duplicate processing
Cache Use Cases ที่ใช้จริง
User Session & Token
เก็บ session data และ JWT refresh token ใน Redis — fast lookup, auto-expire ตาม TTL, revoke ได้ทันทีด้วย DEL
- Key:
session:{sessionID}→ Hash ของ userID, role, org, permissions - Key:
refresh_token:{tokenID}→ userID + metadata - TTL: 15 นาทีสำหรับ session, 7 วันสำหรับ refresh token
- Revoke: ลบ key ทันที — ไม่ต้องรอ token expire
RBAC Permission Cache
Cache permissions ของ user แทนที่จะ query DB ทุก request — ลด latency 50-100ms ต่อ request
- Key:
rbac:user:{userID}→ SET ของresource:actionstrings - ตรวจด้วย
SISMEMBER rbac:user:123 orders:create— O(1) - Invalidate เมื่อเปลี่ยน role:
DEL rbac:user:{userID} - TTL: 5-15 นาที — balance ระหว่าง performance กับ freshness
API Response Cache
Cache response ของ expensive queries — ลด load ไป database:
- Key:
cache:orders:list:{orgID}:{page}:{limit}→ JSON string ของ response - Key:
cache:report:daily:{date}→ pre-computed report data - TTL: 1-5 นาทีสำหรับ list queries, 1 ชั่วโมงสำหรับ reports
- Invalidate pattern: เมื่อ create/update/delete order →
DEL cache:orders:list:{orgID}:*ด้วย Lua script
Rate Limiting
จำกัด API requests per user/IP — ใช้ Sorted Set หรือ simple counter:
Fixed Window:
- Key:
ratelimit:{userID}:{minute}→ counter INCRทุก request, ตรวจว่าเกิน limit ไหม- TTL: 60 วินาที — auto reset ทุกนาที
Sliding Window (Sorted Set):
- Key:
ratelimit:{userID}→ Sorted Set, score = timestamp ZADDทุก request,ZREMRANGEBYSCOREลบ entries เก่ากว่า windowZCARDนับ requests ใน window — ถ้าเกิน limit return 429
Distributed Locking
ป้องกัน race conditions ใน distributed systems — เช่น ป้องกันส่ง order ซ้ำ, ป้องกัน double processing:
- Key:
lock:order:process:{orderID}→ unique lock ID SET key lockID NX EX 30— acquire lock (NX = set only if not exists)- ถ้าได้ lock → process order →
DEL keyrelease lock - TTL ป้องกัน deadlock — ถ้า process crash lock จะ expire อัตโนมัติ
- ใช้กับ order cancel, withdrawal processing, reconciliation jobs
Real-time Price Cache
เก็บ market data / price feeds ที่ update บ่อย:
- Key:
price:{exchange}:{pair}→ Hash ของ bid, ask, last, volume HSET price:binance:BTC_USDT bid 42000 ask 42001 last 42000.5- TTL: 5-10 วินาที — stale price ดีกว่าไม่มี price แต่ต้องตรวจ freshness
- Pub/Sub: publish price updates ให้ subscribers ที่สนใจ
Order Book Snapshot
Cache order book snapshot สำหรับ quick reads:
- Key:
orderbook:{pair}:bids→ Sorted Set, score = price, member = order data - Key:
orderbook:{pair}:asks→ Sorted Set ZRANGEBYSCOREดึง orders ในช่วงราคา- Update ทุก tick ด้วย
ZADD/ZREM
Idempotency Keys
ป้องกัน duplicate operations สำหรับ financial transactions:
- Key:
idempotency:{clientKey}→ JSON response ของ operation ก่อนหน้า - Client ส่ง unique key → ถ้า key มีอยู่แล้ว return cached response
- TTL: 24 ชั่วโมง — เพียงพอสำหรับ retry scenarios
- สำคัญมากสำหรับ payment, transfer, order creation
Queue / Job Processing
ใช้ Redis List เป็น simple job queue:
LPUSH queue:email jobs— producer เพิ่ม jobBRPOP queue:email 0— consumer blocking pop รอ job- เหมาะกับ lightweight queues — email notifications, webhook retries
- สำหรับ heavy workloads ใช้ Redis Streams หรือ Kafka แทน
ปัญหาที่เจอบ่อย & วิธีแก้
Cache stampede — DB โดน spike เมื่อ cache expire
Cache key สำคัญหมดอายุ → requests ทั้งหมด query DB พร้อมกัน → DB overload
สาเหตุ: popular key (เช่น price data, config) expire พร้อมกัน ทุก request เจอ cache miss แล้วยิง DB พร้อมกัน
วิธีแก้:
- Mutex lock: request แรกที่ miss ถือ lock ไป query DB, requests อื่นรอ → return cached result
- Stale-while-revalidate: serve stale data ไปก่อน แล้ว background refresh
- Jittered TTL: เพิ่ม random offset ให้ TTL เช่น
TTL = 300 + random(0,60)ป้องกัน expire พร้อมกัน
Cache invalidation ไม่ครบ — แสดง data เก่า
Update data ใน DB แล้วแต่ user ยังเห็น data เก่าจาก cache
สาเหตุ: ลืม invalidate cache บาง keys หลัง update, key naming convention ไม่ consistent ทำให้ invalidate ไม่ครบ, race condition ระหว่าง write DB กับ invalidate cache
วิธีแก้:
- ใช้ consistent key naming:
cache:{resource}:{id}ทำให้รู้ว่าต้อง invalidate key ไหน - Invalidate ทันทีหลัง DB write สำเร็จ — ไม่ใช่ก่อน
- สำหรับ list queries: ใช้ pattern delete ด้วย Lua script
SCAN+DEL - ใช้ short TTL เป็น safety net — ถึง invalidation หลุดก็จะ expire เอง
Memory เต็ม — Redis evict keys หรือ reject writes
Redis ใช้ memory เกิน maxmemory ทำให้ data หาย หรือ write fail
สาเหตุ: ไม่ตั้ง TTL ให้ทุก cache key, เก็บ data ใหญ่เกินไป (large JSON), keys สะสมไม่มี cleanup, eviction policy ไม่เหมาะสม
วิธีแก้:
- ตั้ง TTL ให้ทุก cache key — ไม่มี key ไหนควรอยู่ตลอด
- ใช้
maxmemory-policy allkeys-lruสำหรับ pure cache — ลบ key ที่ไม่ได้ใช้นานสุดอัตโนมัติ - Monitor memory:
INFO memoryดูused_memoryvsmaxmemory - ลด value size: เก็บเฉพาะ fields ที่จำเป็น ไม่ cache ทั้ง object
Hot key — single key รับ load หนักเกินไป
Key เดียว (เช่น price data ของ BTC/USDT) ถูก read หลายหมื่น requests per second
สาเหตุ: popular data ถูกเก็บใน key เดียว, Redis เป็น single-threaded — key เดียวถูก process บน core เดียว
วิธีแก้:
- Local cache (L1): cache ใน application memory ก่อน (Go map + sync.RWMutex, TTL 1-5 วินาที) ลด Redis calls
- Key replication: กระจาย key เป็น
price:BTC_USDT:{shard}แล้ว random read - ใช้ Redis Cluster — hot key ยังอยู่ node เดียว แต่ลด load key อื่นออก
KEYS command ทำ Redis ค้าง
ใช้ KEYS pattern ใน production แล้ว Redis ไม่ตอบ
สาเหตุ: KEYS scan ทุก key ใน database — ถ้ามี 10M+ keys จะ block Redis หลายวินาที เพราะ Redis เป็น single-threaded
วิธีแก้:
- ใช้
SCAN cursor MATCH pattern COUNT 100แทน — iterate ทีละ batch ไม่ block - ใน production ห้ามใช้
KEYSเด็ดขาด — rename command ใน configrename-command KEYS "" - ใช้
MONITORเฉพาะ debug เท่านั้น — กิน performance เช่นกัน
Connection pool exhaustion — app connect Redis ไม่ได้
Error connection pool exhausted หรือ too many connections
สาเหตุ: ไม่ใช้ connection pool (สร้าง connection ใหม่ทุก request), pool size เล็กเกินไป, ลืม return connection กลับ pool, Redis maxclients ต่ำเกินไป
วิธีแก้:
- ใช้ connection pool เสมอ: Go ใช้
go-redisที่มี pool built-in, Node.js ใช้ioredis - ตั้ง pool size เหมาะสม:
PoolSize = runtime.NumCPU() * 10เป็น starting point - ตรวจ
INFO clientsดูconnected_clients— ถ้าสูงผิดปกติแปลว่า connection leak - ตั้ง
maxclientsใน Redis config ให้เพียงพอ (default 10000)