Skip to content
← All Skills
🔴

Redis

Data & MessagingOfficial Site ›

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

bash
# เข้า 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 clients

Key-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 trips
  • INCR / DECR — atomic counter
  • EXISTS — check key existence
  • TYPE — ดู 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

  • ListLPUSH/RPUSH (add), LPOP/RPOP (remove), LRANGE (read range), BLPOP (blocking pop สำหรับ queue)
  • SetSADD, 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 แบบ atomic
  • WATCH — 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 encoding
  • MEMORY USAGE key ดู memory ของแต่ละ key
  • Short key names, maxmemory config, 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:action strings
  • ตรวจด้วย 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 เก่ากว่า window
  • ZCARD นับ 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 key release 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 เพิ่ม job
  • BRPOP 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_memory vs maxmemory
  • ลด 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 ใน config rename-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)