Redis คืออะไร
ถ้าให้สรุปสั้นที่สุด — Redis คือ "key-value store ใน RAM"
ทำงานเร็วระดับ submillisecond เพราะข้อมูลอยู่ใน memory ไม่ใช่ disk เหมือน PostgreSQL/MySQL
ใช้ทำได้หลายอย่าง:
- Cache — เก็บผลลัพธ์ของ query ที่ช้า ไม่ต้อง hit database ทุกครั้ง
- Session — เก็บ session ของ user แทน in-memory ของ app
- Queue — ส่ง job ไปทำ background
- Rate limit — นับ request ต่อ user ต่อเวลา
- Pub/Sub — broadcast event ระหว่าง service
- Leaderboard — sorted set เก็บคะแนนแล้ว query rank ได้เร็วมาก
Data type ที่ใช้บ่อย
Redis เก็บได้หลาย type ไม่ใช่แค่ string:
# String — basic key-value
SET name "John"
GET name
SET counter 0
INCR counter # เพิ่ม 1 — atomic
EXPIRE name 60 # ลบใน 60 วินาที
# List — queue / stack
LPUSH queue "task1"
LPUSH queue "task2"
RPOP queue # = "task1" (FIFO ถ้าใช้ RPOP)
# Hash — เหมือน object
HSET user:1 name "John" email "[email protected]"
HGET user:1 name
HGETALL user:1
# Set — collection ที่ unique
SADD tags "redis" "cache" "redis"
SMEMBERS tags # = {"redis", "cache"}
# Sorted Set — เก็บพร้อม score เรียงตาม score
ZADD leaderboard 100 "alice" 85 "bob" 95 "carol"
ZRANGE leaderboard 0 -1 WITHSCORES # เรียง score น้อย→มาก
ZREVRANGE leaderboard 0 2 # top 3
ติดตั้ง
วิธีง่ายสุด — Docker:
docker run -d --name redis -p 6379:6379 redis:7-alpine
# หรือใน compose
services:
redis:
image: redis:7-alpine
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redisdata:/data
ports:
- "6379:6379"
volumes:
redisdata:
ทดสอบ:
docker exec -it redis redis-cli
> AUTH yourpassword
> PING
PONG
ใช้กับ Node.js
npm install ioredis
import Redis from 'ioredis'
const redis = new Redis({
host: 'localhost',
port: 6379,
password: process.env.REDIS_PASSWORD,
})
await redis.set('foo', 'bar', 'EX', 60) // ลบใน 60 วินาที
const value = await redis.get('foo')
Pattern 1: Cache database query
ถ้า query ช้า เก็บผลใน Redis ครั้งแรก ครั้งถัดไปอ่านจาก Redis:
async function getUser(id: string) {
const cacheKey = `user:${id}`
// ลอง cache ก่อน
const cached = await redis.get(cacheKey)
if (cached) return JSON.parse(cached)
// ถ้าไม่มี — query database
const user = await db.user.findUnique({ where: { id } })
if (!user) return null
// เก็บใน cache 5 นาที
await redis.set(cacheKey, JSON.stringify(user), 'EX', 300)
return user
}
อย่าลืม invalidate cache ตอนข้อมูลเปลี่ยน:
async function updateUser(id: string, data: any) {
const user = await db.user.update({ where: { id }, data })
await redis.del(`user:${id}`) // invalidate
return user
}
Pattern 2: Session store
แทนที่ใช้ in-memory session (หายตอน restart, ใช้กับ scale ไม่ได้) เก็บใน Redis:
import session from 'express-session'
import RedisStore from 'connect-redis'
app.use(session({
store: new RedisStore({ client: redis }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 },
}))
ดี: scale app เป็นหลาย instance ก็ใช้ session ร่วมกันได้, restart ไม่หาย
Pattern 3: Rate limit
จำกัดให้ user เรียก API ได้ไม่เกิน N ครั้ง/นาที:
async function rateLimit(userId: string, limit = 60, windowSec = 60) {
const key = `ratelimit:${userId}:${Math.floor(Date.now() / 1000 / windowSec)}`
const current = await redis.incr(key)
if (current === 1) await redis.expire(key, windowSec)
if (current > limit) throw new Error('Too many requests')
}
// ใน middleware
app.use(async (req, res, next) => {
try {
await rateLimit(req.user.id, 60, 60)
next()
} catch (e) {
res.status(429).json({ error: 'Too many requests' })
}
})
Pattern 4: Queue / Background job
ใช้ library อย่าง BullMQ สร้าง queue บน Redis:
import { Queue, Worker } from 'bullmq'
const emailQueue = new Queue('email', {
connection: { host: 'redis', port: 6379 },
})
// Producer (จากใน API)
await emailQueue.add('welcome', {
to: '[email protected]',
subject: 'Welcome!',
})
// Worker (รัน process แยก)
new Worker('email', async (job) => {
await sendEmail(job.data)
}, {
connection: { host: 'redis', port: 6379 },
})
ดี: ส่งอีเมลไม่ block API, retry ได้, scale worker แยกได้
Pattern 5: Distributed lock
ป้องกัน 2 process ทำงานเดียวกันพร้อมกัน:
async function withLock(key: string, ttl: number, fn: () => Promise<void>) {
const lockKey = `lock:${key}`
const acquired = await redis.set(lockKey, '1', 'EX', ttl, 'NX')
if (!acquired) throw new Error('Locked')
try {
await fn()
} finally {
await redis.del(lockKey)
}
}
// ใช้ใน cron
await withLock('daily-report', 300, async () => {
await generateDailyReport()
})
SET ... NX = set ถ้ายังไม่มี key — เป็น atomic operation
Persistence — Redis ลืมข้อมูลตอน restart มั้ย
Redis มี 2 mode:
RDB (snapshot) — save full database ลง disk เป็นช่วงๆ (default ทุก 5 นาที ถ้ามี write)
AOF (append-only file) — log ทุก command ลง disk = restore เกือบเหมือนเดิม
production แนะนำเปิด AOF:
redis-server --appendonly yes
แต่ — Redis ไม่ใช่ที่เก็บข้อมูลหลัก data ที่สำคัญต้องอยู่ใน database จริง Redis เป็น cache + helper
ข้อควรระวัง
RAM full = Redis crash ตั้ง maxmemory + eviction policy:
maxmemory 512mb
maxmemory-policy allkeys-lru # ลบ key เก่าที่ไม่ใช้นาน
Key ใหญ่เกิน = block server (Redis เป็น single-thread) อย่าเก็บ value ขนาด MB
SCAN แทน KEYS เวลามี key เยอะ — KEYS * block server ใช้ SCAN 0 ดีกว่า
ใส่ TTL ทุก key ที่ทำเป็น cache — ไม่งั้น RAM เต็มแน่นอน
Alternative
Memcached — เก่าแต่ยังใช้กัน เร็วกว่านิดถ้าใช้แค่ key-value แต่ feature น้อยกว่า
Valkey — fork ของ Redis หลัง Redis เปลี่ยน license ปี 2024 — API เหมือน Redis เป๊ะ
KeyDB — fork ของ Redis แบบ multi-thread
ทั้งหมดใช้ protocol/command เดียวกันกับ Redis สลับใช้ได้
สรุป
ถ้า project มี user ใช้พอประมาณ Redis เกือบจะ default แล้ว — ตั้ง 5 นาทีบน VPS เดียวกับ app ก็ได้ ไม่ต้องคิดมาก
start จากแค่ใช้เป็น cache → พอเข้าใจ pattern → ใช้ทำ rate limit, session, queue ได้ครบ
อ่านเพิ่ม: Docker Compose ใช้จริง — มี example config Redis ใน production