Back to blog

Redis vs Memcached vs Local Cache: Chọn Giải Pháp Caching Phù Hợp

rediscachingbackendperformanceinfrastructure
Redis vs Memcached vs Local Cache: Chọn Giải Pháp Caching Phù Hợp

Giới Thiệu

Ứng dụng của bạn đang chậm. Mỗi database query mất 50ms. Bạn đang thực hiện cùng một query hàng ngàn lần mỗi giây. Câu trả lời rõ ràng là: caching.

Nhưng nên dùng giải pháp caching nào?

Ba lựa chọn phổ biến nhất:

Giải phápLoạiCông cụ ví dụ
Local CacheIn-process memoryGuava Cache, Caffeine, lru-cache, Map
RedisDistributed in-memory storeRedis, Valkey, KeyDB
MemcachedDistributed memory cacheMemcached

Mỗi loại có điểm mạnh riêng. Mỗi loại có trade-off. Và hệ thống tốt nhất thường dùng nhiều hơn một loại. Bài viết này sẽ giúp bạn hiểu khi nào nên dùng từng loại — và khi nào nên kết hợp chúng.

Những Gì Bạn Sẽ Học

✅ Cách local cache, Redis và Memcached hoạt động bên dưới
✅ Đặc điểm performance và so sánh latency
✅ Khi nào chọn từng giải pháp (với real-world scenarios)
✅ Multi-tier caching architecture patterns
✅ Ví dụ implementation thực tế bằng Node.js, Python và Java
✅ Cache invalidation strategies cho từng layer
✅ Các pitfalls phổ biến và cách tránh

Yêu Cầu


Phần 1: Hiểu Ba Lựa Chọn

Local Cache (In-Process)

Local cache sống bên trong process ứng dụng. Nó lưu data trong chính bộ nhớ (heap) của application, khiến nó trở thành loại cache nhanh nhất — không có network call, không serialization.

Cách hoạt động:

// Node.js — simple in-memory cache với TTL
const cache = new Map<string, { value: unknown; expiresAt: number }>();
 
function localGet(key: string): unknown | null {
  const entry = cache.get(key);
  if (!entry) return null;
  if (Date.now() > entry.expiresAt) {
    cache.delete(key);
    return null;
  }
  return entry.value;
}
 
function localSet(key: string, value: unknown, ttlMs: number): void {
  cache.set(key, { value, expiresAt: Date.now() + ttlMs });
}
 
// Sử dụng
localSet("user:123", { name: "Alice", role: "admin" }, 60_000); // 60s TTL
const user = localGet("user:123"); // ~microseconds, không qua network

Các thư viện production-grade:

// Java — Caffeine (kế thừa Guava Cache)
LoadingCache<String, User> cache = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(Duration.ofMinutes(5))
    .recordStats() // Bật metrics hit/miss
    .build(key -> userRepository.findById(key));
 
User user = cache.get("user:123"); // Tự động load từ DB khi miss
# Python — cachetools
from cachetools import TTLCache
 
cache = TTLCache(maxsize=10_000, ttl=300)  # 300s TTL
 
def get_user(user_id: str) -> dict:
    if user_id in cache:
        return cache[user_id]
    user = db.find_user(user_id)
    cache[user_id] = user
    return user

Điểm mạnh:

  • Nhanh nhất có thể: Không network latency, không serialization (~1μs so với ~1ms cho Redis)
  • Đơn giản nhất để setup: Không cần infrastructure bên ngoài
  • Không tốn chi phí vận hành: Không cần deploy, monitor server riêng

Điểm yếu:

  • Không shared: Mỗi app instance có cache riêng — data bị duplicate, state không nhất quán
  • Bị giới hạn bởi memory: Cạnh tranh heap space với application
  • Mất khi restart: Cache biến mất khi process chết
  • Không có built-in eviction policies (trừ khi dùng library như Caffeine)

Redis

Redis là distributed in-memory data structure store. Nó chạy như một process riêng biệt (thường trên server chuyên dụng) và tất cả app instance kết nối tới nó qua network.

Cách hoạt động:

// Node.js — ioredis
import Redis from "ioredis";
 
const redis = new Redis({ host: "redis.internal", port: 6379 });
 
// Cache string đơn giản
await redis.set("user:123", JSON.stringify(user), "EX", 300); // 5 phút TTL
const cached = await redis.get("user:123");
const user = cached ? JSON.parse(cached) : null;
 
// Hash — lưu structured data mà không cần serialization
await redis.hset("user:123", { name: "Alice", role: "admin", loginCount: "42" });
const name = await redis.hget("user:123", "name"); // Lấy single field
# Python — redis-py
import redis
import json
 
r = redis.Redis(host="redis.internal", port=6379, decode_responses=True)
 
# Cache với TTL
r.setex("user:123", 300, json.dumps({"name": "Alice", "role": "admin"}))
user = json.loads(r.get("user:123"))
 
# Atomic counter — không race conditions
r.incr("api:rate:192.168.1.1")  # Thread-safe increment
r.expire("api:rate:192.168.1.1", 60)  # Reset mỗi 60s

Điểm mạnh:

  • Rich data structures: Strings, Hashes, Lists, Sets, Sorted Sets, Streams, HyperLogLog
  • Shared giữa các instance: Tất cả app server đọc cùng một cache
  • Persistence options: RDB snapshots và AOF logging sống sót qua restart
  • Atomic operations: INCR, LPUSH, SADD — không race conditions
  • Pub/Sub: Built-in messaging để gửi cache invalidation events
  • Lua scripting: Complex operations trong một round trip duy nhất
  • Replication & Clustering: High availability built-in

Điểm yếu:

  • Network latency: ~1ms round trip so với ~1μs cho local cache
  • Chi phí serialization: Phải convert objects sang strings/bytes rồi convert ngược lại
  • Phức tạp khi vận hành: Thêm một service phải deploy, monitor, maintain
  • Single-threaded (chủ yếu): Một CPU core xử lý tất cả commands (I/O threads được thêm từ Redis 6+)
  • Chi phí RAM: RAM đắt khi scale lên

Memcached

Memcached là distributed memory caching system được thiết kế cho một mục đích: caching. Nó đơn giản hơn Redis bởi thiết kế — một pure key-value store không có data structures, không persistence, không scripting.

// Node.js — memjs
import memjs from "memjs";
 
const mc = memjs.Client.create("memcached1:11211,memcached2:11211");
 
await mc.set("user:123", JSON.stringify(user), { expires: 300 });
const { value } = await mc.get("user:123");
const user = value ? JSON.parse(value.toString()) : null;
# Python — pymemcache
from pymemcache.client.hash import HashClient
 
client = HashClient([
    ("memcached1", 11211),
    ("memcached2", 11211),
])
 
client.set("user:123", json.dumps(user), expire=300)
cached = client.get("user:123")
user = json.loads(cached) if cached else None

Điểm mạnh:

  • Multi-threaded: Tận dụng tất cả CPU cores (khác với Redis single-threaded)
  • Memory usage dự đoán được: Slab allocator ngăn fragmentation
  • Đơn giản và battle-tested: Ít moving parts = ít bất ngờ
  • Horizontal scaling: Consistent hashing giữa nhiều nodes
  • Memory overhead thấp hơn mỗi key: Không có data structure metadata

Điểm yếu:

  • Chỉ strings: Không hashes, lists, sets, hay sorted sets
  • Không persistence: Data mất khi process restart
  • Không replication: Không built-in master-replica failover
  • Không Pub/Sub: Không event notifications cho cache changes
  • Giới hạn value 1MB: Objects lớn phải chunk thủ công
  • Không Lua scripting: Không complex atomic operations

Phần 2: So Sánh Trực Tiếp

Bảng So Sánh Tính Năng

Tính năngLocal CacheRedisMemcached
Latency~1μs~0.5-1ms~0.5-1ms
Data StructuresTùy ngôn ngữRich (Strings, Hash, List, Set, ZSet, Stream)Chỉ Strings
Shared giữa instances❌ Không✅ Có✅ Có
Persistence❌ Không✅ RDB + AOF❌ Không
Max Value SizeGiới hạn heap512MB1MB (mặc định)
ThreadingApp threadsSingle-threaded*Multi-threaded
Replication❌ Không✅ Built-in❌ Không
Pub/Sub❌ Không✅ Built-in❌ Không
Eviction PoliciesTùy library8 policies (LRU, LFU, v.v.)Chỉ LRU
Cluster Mode❌ Không✅ Redis ClusterClient-side hashing
Scripting❌ Không✅ Lua / Functions❌ Không
Memory EfficiencyTốt nhất (không serialization)TốtTốt hơn Redis**

* Redis 6+ có I/O threads cho network handling, nhưng command execution vẫn single-threaded.
** Slab allocator của Memcached hiệu quả hơn về memory cho simple key-value pairs.

Performance Benchmarks

So sánh latency thực tế (cùng hardware, cùng network):

Operation           Local Cache    Redis         Memcached
─────────────────────────────────────────────────────────
Simple GET          0.001ms        0.3-0.8ms     0.3-0.7ms
Simple SET          0.001ms        0.3-0.8ms     0.3-0.7ms
Batch GET (100)     0.01ms         1-3ms         1-2ms
1KB value GET       0.001ms        0.4-1ms       0.3-0.8ms
100KB value GET     0.001ms        1-3ms         0.8-2ms
Counter INCREMENT   0.001ms        0.3-0.8ms     0.3-0.7ms

Key takeaway: Local cache nhanh hơn 100-1000x so với bất kỳ giải pháp network-based nào. Redis và Memcached tương đương cho simple operations, nhưng Memcached nhỉnh hơn một chút cho pure key-value workloads nhờ multi-threaded architecture.

So Sánh Throughput

Trên single server với 32 cores:

                    Redis           Memcached
────────────────────────────────────────────
GET ops/sec         100,000-200,000   200,000-600,000
SET ops/sec         100,000-200,000   200,000-500,000
Concurrent clients  10,000+           50,000+

Multi-threaded design của Memcached cho throughput gấp 2-3 lần cho simple get/set operations. Tuy nhiên, Redis bù đắp bằng pipelining, Lua scripts, và data structures giúp giảm round trips.


Phần 3: Khi Nào Dùng Gì

Dùng Local Cache Khi...

1. Bạn cần tốc độ đọc nhanh nhất có thể:

// Hot configuration mà mỗi request đều cần
const configCache = new Map<string, AppConfig>();
 
function getConfig(): AppConfig {
  const cached = configCache.get("app-config");
  if (cached) return cached; // ~1μs — không qua network
 
  const config = loadFromDatabase();
  configCache.set("app-config", config);
  return config;
}

2. Data chủ yếu đọc và hiếm khi thay đổi:

  • Feature flags
  • Application configuration
  • Static reference data (country codes, currency rates cập nhật hàng ngày)
  • Compiled templates hoặc regex patterns

3. Sự không nhất quán giữa các instance là chấp nhận được:

  • Mỗi server có thể có cache state hơi khác nhau trong khoảng thời gian ngắn
  • Ví dụ: view counts của blog post không cần perfectly synchronized

4. Bạn muốn không phụ thuộc infrastructure bên ngoài:

  • Local development không cần Docker
  • Serverless functions (Lambda, Cloud Functions) nơi external connections tốn kém
  • Edge computing với network access hạn chế

Dùng Redis Khi...

1. Nhiều app instance cần shared state:

// Session management — user có thể hit bất kỳ server nào
await redis.hset(`session:${sessionId}`, {
  userId: "123",
  role: "admin",
  lastAccess: Date.now().toString(),
});
await redis.expire(`session:${sessionId}`, 3600); // 1 giờ
 
// Bất kỳ server nào cũng đọc được session này
const session = await redis.hgetall(`session:${sessionId}`);

2. Bạn cần data structures vượt xa key-value đơn giản:

# Leaderboard với sorted sets
r.zadd("leaderboard:weekly", {"alice": 2500, "bob": 1800, "charlie": 3200})
top_10 = r.zrevrange("leaderboard:weekly", 0, 9, withscores=True)
 
# Rate limiting với atomic operations
pipe = r.pipeline()
pipe.incr(f"rate:{user_id}")
pipe.expire(f"rate:{user_id}", 60)
current, _ = pipe.execute()
if current > 100:
    raise RateLimitExceeded()
 
# Job queue với lists
r.lpush("jobs:email", json.dumps({"to": "[email protected]", "template": "welcome"}))
job = r.brpop("jobs:email", timeout=30)  # Blocking pop — worker chờ jobs

3. Bạn cần persistence và high availability:

  • Cache warmup sau restart (RDB/AOF recovery)
  • Master-replica failover với Redis Sentinel
  • Cross-datacenter replication

4. Bạn cần Pub/Sub cho cache invalidation:

// Publisher — khi data thay đổi
await redis.publish("cache:invalidate", JSON.stringify({
  key: "user:123",
  action: "update",
}));
 
// Subscriber — tất cả app instance lắng nghe
const sub = redis.duplicate();
sub.subscribe("cache:invalidate");
sub.on("message", (channel, message) => {
  const { key } = JSON.parse(message);
  localCache.delete(key); // Invalidate local cache
});

Dùng Memcached Khi...

1. Bạn chỉ cần simple key-value caching ở quy mô cực lớn:

# Use case của Facebook: hàng tỷ simple key-value lookups
# Profile data, feed items, social graph edges
client.set(f"profile:{user_id}", serialize(profile), expire=3600)
profile = deserialize(client.get(f"profile:{user_id}"))

2. Bạn cần throughput tối đa trên multi-core servers:

  • Multi-threaded architecture của Memcached tận dụng tất cả cores
  • Lý tưởng khi bạn có máy 32+ cores chuyên dụng cho caching

3. Bạn muốn caching layer đơn giản nhất:

  • Không clustering phải configure
  • Không persistence phải manage
  • Không replication phải monitor
  • Chỉ raw speed cho simple get/set

4. Memory efficiency là yếu tố quyết định cho datasets lớn:

  • Slab allocator của Memcached tối ưu cho uniform-sized objects
  • Memory overhead per key thấp hơn Redis

Sơ Đồ Quyết Định


Phần 4: Multi-Tier Caching Architecture

Hệ thống tốt nhất không chọn một — chúng kết hợp cả ba trong kiến trúc phân tầng.

Cache Hierarchy

Đây chính xác là architecture trong diagram ở đầu bài — users hit CDN (static cache), rồi load balancer phân phối tới app servers (mỗi server có local cache), chia sẻ một Redis cache layer, backed bởi database với read replicas.

Implementation: Two-Tier Cache

import Redis from "ioredis";
import { LRUCache } from "lru-cache";
 
// L1: Local cache — nhỏ, nhanh, per-instance
const localCache = new LRUCache<string, unknown>({
  max: 5_000,           // Tối đa 5000 entries
  ttl: 30_000,          // 30 giây TTL (ngắn — giảm stale data)
});
 
// L2: Redis — shared giữa tất cả instances
const redis = new Redis("redis://redis.internal:6379");
 
async function tieredGet<T>(key: string): Promise<T | null> {
  // L1: Check local cache trước (~1μs)
  const local = localCache.get(key) as T | undefined;
  if (local !== undefined) {
    return local;
  }
 
  // L2: Check Redis (~1ms)
  const remote = await redis.get(key);
  if (remote) {
    const parsed = JSON.parse(remote) as T;
    localCache.set(key, parsed); // Promote lên L1
    return parsed;
  }
 
  // L3: Cache miss — caller nên fetch từ database
  return null;
}
 
async function tieredSet<T>(key: string, value: T, ttlSec: number): Promise<void> {
  // Ghi vào cả hai layers
  localCache.set(key, value);
  await redis.set(key, JSON.stringify(value), "EX", ttlSec);
}
 
async function tieredInvalidate(key: string): Promise<void> {
  localCache.delete(key);
  await redis.del(key);
  // Thông báo các instance khác xóa local cache
  await redis.publish("cache:invalidate", key);
}

Implementation: Full Cache-Aside Pattern

import json
import redis
from cachetools import TTLCache
 
# L1: Local cache
local_cache = TTLCache(maxsize=5000, ttl=30)
 
# L2: Redis
redis_client = redis.Redis(host="redis.internal", decode_responses=True)
 
def get_user(user_id: str) -> dict | None:
    cache_key = f"user:{user_id}"
 
    # L1: Local cache (~1μs)
    if cache_key in local_cache:
        return local_cache[cache_key]
 
    # L2: Redis (~1ms)
    cached = redis_client.get(cache_key)
    if cached:
        user = json.loads(cached)
        local_cache[cache_key] = user  # Promote lên L1
        return user
 
    # L3: Database (~10ms)
    user = db.query("SELECT * FROM users WHERE id = %s", user_id)
    if user:
        redis_client.setex(cache_key, 300, json.dumps(user))  # 5 phút trong Redis
        local_cache[cache_key] = user  # 30s trong local
    return user
 
def update_user(user_id: str, data: dict) -> None:
    db.execute("UPDATE users SET ... WHERE id = %s", user_id)
    cache_key = f"user:{user_id}"
 
    # Invalidate cả hai layers
    local_cache.pop(cache_key, None)
    redis_client.delete(cache_key)
 
    # Thông báo các instance khác
    redis_client.publish("cache:invalidate", cache_key)

Cache Invalidation Giữa Các Instances

Thách thức lớn nhất với multi-tier caching là giữ local caches đồng bộ. Khi data thay đổi trên một server, local cache của các server khác trở nên stale.

// Lắng nghe invalidation events trên mỗi app instance
const subscriber = redis.duplicate();
subscriber.subscribe("cache:invalidate");
subscriber.on("message", (channel: string, key: string) => {
  localCache.delete(key);
  console.log(`Invalidated local cache: ${key}`);
});

TTL như safety net: Ngay cả khi không có explicit invalidation, TTL ngắn trên local cache (15-60 giây) đảm bảo stale data không tồn tại lâu.


Phần 5: Các Pitfalls Phổ Biến

1. Cache Stampede (Thundering Herd)

Khi một cache key phổ biến hết hạn, hàng trăm concurrent requests đồng thời miss cache và đổ vào database cùng lúc.

Giải pháp:

// Giải pháp 1: Mutex lock — chỉ một request fetch từ DB
const locks = new Map<string, Promise<unknown>>();
 
async function getWithLock<T>(key: string, fetchFn: () => Promise<T>): Promise<T> {
  // Check cache
  const cached = await tieredGet<T>(key);
  if (cached) return cached;
 
  // Check nếu request khác đang fetch
  const existing = locks.get(key);
  if (existing) return existing as Promise<T>;
 
  // Request này thắng — fetch từ DB
  const promise = fetchFn().then(async (value) => {
    await tieredSet(key, value, 300);
    locks.delete(key);
    return value;
  });
 
  locks.set(key, promise);
  return promise;
}
# Giải pháp 2: Early expiration — refresh trước khi TTL hết
def get_with_early_refresh(key: str, fetch_fn, ttl: int = 300) -> any:
    cached = redis_client.get(key)
    if cached:
        data = json.loads(cached)
        remaining_ttl = redis_client.ttl(key)
 
        # Refresh nếu còn dưới 20% TTL
        if remaining_ttl < ttl * 0.2:
            threading.Thread(target=_refresh_cache, args=(key, fetch_fn, ttl)).start()
 
        return data
 
    # Cache miss
    value = fetch_fn()
    redis_client.setex(key, ttl, json.dumps(value))
    return value

2. Cache Penetration

Requests cho data không tồn tại bypass cache mỗi lần và hit thẳng database.

// ❌ Sai: Mỗi request cho user không tồn tại đều hit DB
async function getUser(id: string) {
  const cached = await redis.get(`user:${id}`);
  if (cached) return JSON.parse(cached);
  const user = await db.findUser(id); // Trả về null — không cache gì
  return user;
}
 
// ✅ Đúng: Cache cả null results
async function getUser(id: string) {
  const cached = await redis.get(`user:${id}`);
  if (cached === "NULL") return null; // Cached negative result
  if (cached) return JSON.parse(cached);
 
  const user = await db.findUser(id);
  if (user) {
    await redis.set(`user:${id}`, JSON.stringify(user), "EX", 300);
  } else {
    await redis.set(`user:${id}`, "NULL", "EX", 60); // TTL ngắn cho negatives
  }
  return user;
}

3. Hot Key Problem

Một single key nhận traffic khổng lồ (celebrity profile, viral post) có thể áp đảo một Redis node.

// Giải pháp: Replicate hot keys qua nhiều local caches + random suffix
async function getHotKey<T>(baseKey: string, fetchFn: () => Promise<T>): Promise<T> {
  // L1: Luôn check local cache trước — phân tán tải
  const local = localCache.get(baseKey);
  if (local) return local as T;
 
  // L2: Spread qua Redis replicas với random suffix
  const shardKey = `${baseKey}:shard:${Math.floor(Math.random() * 3)}`;
  const cached = await redis.get(shardKey);
  if (cached) {
    const value = JSON.parse(cached) as T;
    localCache.set(baseKey, value); // Hấp thụ future hits locally
    return value;
  }
 
  const value = await fetchFn();
  // Ghi vào tất cả shards
  await Promise.all([
    redis.set(`${baseKey}:shard:0`, JSON.stringify(value), "EX", 300),
    redis.set(`${baseKey}:shard:1`, JSON.stringify(value), "EX", 300),
    redis.set(`${baseKey}:shard:2`, JSON.stringify(value), "EX", 300),
  ]);
  localCache.set(baseKey, value);
  return value;
}

4. Serialization Overhead

Convert objects sang JSON rồi convert ngược lại tốn CPU nhiều hơn bạn nghĩ.

// ❌ Chậm: JSON.parse mỗi cache hit
const user = JSON.parse(await redis.get("user:123")); // ~0.5ms cho objects lớn
 
// ✅ Tốt hơn: Dùng Redis Hashes tránh full serialization
await redis.hset("user:123", { name: "Alice", role: "admin", age: "30" });
const name = await redis.hget("user:123", "name"); // Không cần parsing
 
// ✅ Tốt hơn: Dùng MessagePack cho binary serialization (nhanh 2-3x so với JSON)
import { encode, decode } from "@msgpack/msgpack";
await redis.setBuffer("user:123", Buffer.from(encode(user)));
const cached = await redis.getBuffer("user:123");
const user = decode(cached);

5. Memory Pressure

Local caches tăng không kiểm soát sẽ cướp memory của application.

// ❌ Nguy hiểm: cache không giới hạn
Map<String, Object> cache = new HashMap<>(); // Tăng mãi → OutOfMemoryError
 
// ✅ An toàn: giới hạn với eviction
LoadingCache<String, Object> cache = Caffeine.newBuilder()
    .maximumSize(10_000)          // Hard cap
    .expireAfterWrite(Duration.ofMinutes(5))
    .maximumWeight(50_000_000)    // Giới hạn 50MB
    .weigher((key, value) -> estimateSize(value))
    .removalListener((key, value, cause) ->
        log.info("Evicted {} due to {}", key, cause))
    .build(key -> loadFromDb(key));

Phần 6: Kiến Trúc Trong Thực Tế

Ứng Dụng Nhỏ (< 1K RPM)

Chỉ cần local cache. Redis là overkill cho một server xử lý vài trăm requests mỗi phút.

Ứng Dụng Trung Bình (1K-100K RPM)

Thêm Redis làm shared cache layer. Nhiều app instances cần cache state nhất quán.

Ứng Dụng Lớn (100K+ RPM)

Full multi-tier: CDN → Load Balancer → App Servers (có local cache) → Redis Cluster → Database với read replicas. Đây chính là architecture từ diagram ở đầu bài.

Quy Mô Khổng Lồ (Level Facebook/Twitter)

Ở quy mô cực lớn, các công ty dùng Memcached cho simple, high-throughput key-value lookups (profile data, feed items) và Redis cho structured data (leaderboards, sessions, rate limiting). Local cache nằm phía trước cả hai.


Tổng Kết

Bảng Tham Khảo Nhanh

Tình huốngLựa chọn tốt nhất
Single server, read-heavy configLocal Cache
Multi-server, cần shared stateRedis
Cần data structures (sorted sets, lists)Redis
Cần persistence qua restartRedis
Simple get/set ở throughput khổng lồMemcached
Pub/Sub cho cache invalidationRedis
Serverless / edge computingLocal Cache
Tiết kiệm chi phí, single instanceLocal Cache
Đã dùng Redis cho features khácRedis (gom lại)

Nguyên Tắc Vàng

Bắt đầu đơn giản, thêm layers khi có bằng chứng cần thiết:

  1. Bắt đầu với local cache — miễn phí, nhanh, không cần infrastructure
  2. Thêm Redis khi cần shared state giữa nhiều servers
  3. Thêm Memcached chỉ khi throughput của Redis trở thành bottleneck cho simple key-value operations
  4. Kết hợp các layers khi cần cả tốc độ tối đa (local) và consistency (distributed)

Tiếp Theo

Bây giờ bạn đã hiểu khi nào dùng từng giải pháp caching:

Caching là một trong những topic mà lý thuyết thì đơn giản nhưng "con quỷ nằm trong chi tiết". Cách tốt nhất để học là bắt đầu với local cache, đo hit rates, và chỉ thêm complexity khi thực sự cần. Premature optimization áp dụng cho caching architecture luôn đó.

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.