data9 phút đọc
Redis Caching Patterns cho Ứng dụng Node.js
Caching production với Redis — cache-aside, write-through, session management, rate limiting với sorted sets, pub/sub và chiến lược invalidation.
V
Bởi Ventra Rocket#Redis#Caching#Node.js#Performance#Backend
Redis là caching layer phổ biến nhất cho Node.js, với sorted sets, pub/sub và atomic operations.
Khởi tạo
import { createClient } from "redis";
const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();
1. Cache-Aside Pattern
class ProductService {
private readonly TTL = 3600;
async getProduct(id: string): Promise<Product> {
const key = `product:${id}`;
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const product = await db.products.findUnique({ where: { id } });
if (!product) throw new Error("Không tìm thấy");
await redis.setEx(key, this.TTL, JSON.stringify(product));
return product;
}
async updateProduct(id: string, data: Partial<Product>) {
const product = await db.products.update({ where: { id }, data });
await redis.del(`product:${id}`); // Xóa cache khi cập nhật
return product;
}
}
2. Ngăn chặn Cache Stampede
async function getWithLock<T>(key: string, ttl: number, fn: () => Promise<T>) {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const lockKey = `lock:${key}`;
const acquired = await redis.set(lockKey, "1", { EX: 10, NX: true });
if (acquired) {
try {
const data = await fn();
await redis.setEx(key, ttl, JSON.stringify(data));
return data;
} finally {
await redis.del(lockKey);
}
}
await new Promise((r) => setTimeout(r, 100));
return getWithLock(key, ttl, fn); // Retry
}
3. Quản lý Session
const TTL = 86400; // 24 giờ
async function createSession(userId: string) {
const id = crypto.randomUUID();
await redis.setEx(`session:${id}`, TTL, JSON.stringify({ userId }));
return id;
}
async function getSession(id: string) {
const raw = await redis.get(`session:${id}`);
if (!raw) return null;
await redis.expire(`session:${id}`, TTL); // Sliding expiration
return JSON.parse(raw);
}
4. Rate Limiting với Sorted Set
async function checkRateLimit(id: string, limit: number, windowSec: number) {
const key = `ratelimit:${id}`;
const now = Date.now();
const windowStart = now - windowSec * 1000;
const pipeline = redis.multi();
pipeline.zRemRangeByScore(key, "-inf", windowStart);
pipeline.zAdd(key, { score: now, value: `${now}` });
pipeline.zCard(key);
pipeline.expire(key, windowSec);
const results = await pipeline.exec();
const count = results[2] as number;
return { allowed: count <= limit, remaining: Math.max(0, limit - count) };
}
5. Pub/Sub
const pub = redis.duplicate();
const sub = redis.duplicate();
await Promise.all([pub.connect(), sub.connect()]);
await pub.publish("inventory:updated", JSON.stringify({ productId: "123", qty: 50 }));
await sub.subscribe("inventory:updated", (msg) => {
const event = JSON.parse(msg);
// Cập nhật WebSocket clients
});
Kết luận
Redis phát huy tối đa khi: cache-aside cho DB offloading, sorted sets cho rate limiting, atomic operations cho distributed locks. Luôn đo cache hit rates và lên kế hoạch invalidation trước.
Bài viết liên quan
data
Tối ưu hiệu năng PostgreSQL: Index, Query Plan và Configuration
Tối ưu PostgreSQL cho production — chiến lược index, đọc EXPLAIN ANALYZE, connection pooling, partitioning và cấu hình postgresql.conf.
PostgreSQLDatabasePerformance
20 tháng 2, 2026·10 phút đọc
Đọc thêm →data
Thiết kế Real-time ETL Pipeline: Từ Kafka đến Data Warehouse
Hướng dẫn thiết kế pipeline ETL real-time với Apache Kafka, Flink và Snowflake — xử lý hàng triệu events mỗi giây với độ trễ dưới 1 giây.
ETLKafkaReal-time
5 tháng 2, 2026·10 phút đọc
Đọc thêm →