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áp | Loại | Công cụ ví dụ |
|---|---|---|
| Local Cache | In-process memory | Guava Cache, Caffeine, lru-cache, Map |
| Redis | Distributed in-memory store | Redis, Valkey, KeyDB |
| Memcached | Distributed memory cache | Memcached |
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
- Hiểu cơ bản về cách web application hoạt động
- Quen thuộc với Redis fundamentals (có thì tốt, không bắt buộc)
- Kiến thức cơ bản về cấu trúc dữ liệu key-value
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 networkCá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ăng | Local Cache | Redis | Memcached |
|---|---|---|---|
| Latency | ~1μs | ~0.5-1ms | ~0.5-1ms |
| Data Structures | Tù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 Size | Giới hạn heap | 512MB | 1MB (mặc định) |
| Threading | App threads | Single-threaded* | Multi-threaded |
| Replication | ❌ Không | ✅ Built-in | ❌ Không |
| Pub/Sub | ❌ Không | ✅ Built-in | ❌ Không |
| Eviction Policies | Tùy library | 8 policies (LRU, LFU, v.v.) | Chỉ LRU |
| Cluster Mode | ❌ Không | ✅ Redis Cluster | Client-side hashing |
| Scripting | ❌ Không | ✅ Lua / Functions | ❌ Không |
| Memory Efficiency | Tốt nhất (không serialization) | Tốt | Tố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.7msKey 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ờ jobs3. 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 value2. 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ống | Lựa chọn tốt nhất |
|---|---|
| Single server, read-heavy config | Local Cache |
| Multi-server, cần shared state | Redis |
| Cần data structures (sorted sets, lists) | Redis |
| Cần persistence qua restart | Redis |
| Simple get/set ở throughput khổng lồ | Memcached |
| Pub/Sub cho cache invalidation | Redis |
| Serverless / edge computing | Local Cache |
| Tiết kiệm chi phí, single instance | Local Cache |
| Đã dùng Redis cho features khác | Redis (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:
- Bắt đầu với local cache — miễn phí, nhanh, không cần infrastructure
- Thêm Redis khi cần shared state giữa nhiều servers
- Thêm Memcached chỉ khi throughput của Redis trở thành bottleneck cho simple key-value operations
- 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:
- Đi sâu vào Redis: Học Redis: Hướng Dẫn Toàn Diện
- Redis internals: Redis Source Code Explained
- Persistence: Redis Persistence Internals: RDB và AOF
- Tích hợp framework: Spring Boot Caching với Redis
- System design context: Load Balancing Explained
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.