Mục lục
- Mục Tiêu Bài Học
- Vì Sao TTL Là Một Quyết Định Chiến Lược
- Hard TTL: Hết Hạn Là Xoá
- Soft TTL (Logical Expiry)
- Business TTL: Suy TTL Từ Nghiệp Vụ
- Bảng Gợi Ý TTL Theo Loại Dữ Liệu
- TTL Jitter: Chống Mass-Expiration
- Code ioredis (TypeScript)
- Code redis-py (Python)
- Trade-off Khi Chọn TTL
- Pitfalls & Anti-patterns
- Tổng Kết & Quiz
Mục Tiêu Bài Học
- Hiểu vì sao TTL không phải một con số tuỳ tiện mà là một quyết định chiến lược cân bằng giữa độ tươi dữ liệu, tải DB và bộ nhớ.
- Phân biệt hard TTL (Redis tự xoá key khi hết hạn, lần đọc sau rebuild từ DB) và soft TTL hay logical expiry (lưu kèm timestamp nên refresh, value vẫn còn sau soft-TTL).
- Biết suy ra business TTL từ ba yếu tố: tần suất thay đổi của dữ liệu, mức chấp nhận stale (tolerance staleness) và chi phí rebuild trên DB.
- Nắm kỹ thuật TTL jitter để tránh hàng loạt key hết hạn cùng thời điểm, với công thức
TTL = base + random(0..jitter). - Viết được code set TTL theo từng loại, tính jitter và lưu metadata soft-TTL bằng cả ioredis (TypeScript) và redis-py (Python).
- Đọc được bảng gợi ý TTL theo loại dữ liệu và giải thích lý do đằng sau mỗi con số.
- Nhận diện trade-off (TTL ngắn so với dài) và các anti-pattern thường gặp khi đặt TTL.
Vì Sao TTL Là Một Quyết Định Chiến Lược
Bài 4 đã đặt nguyên tắc TTL-first: mọi key cache đều nên có thời hạn để dữ liệu tự làm mới và bộ nhớ không phình vô hạn. Nhưng "có TTL" mới chỉ là điều kiện cần. Câu hỏi thực sự khó trong production là: đặt TTL bao lâu, và dùng cơ chế hết hạn loại nào cho từng loại dữ liệu?
Một giá trị TTL luôn là sự cân bằng giữa ba lực kéo ngược chiều nhau:
- Độ tươi (freshness): TTL càng ngắn thì cache càng gần với DB, stale window càng hẹp.
- Tải DB: TTL càng ngắn thì key hết hạn càng thường xuyên, mỗi lần hết hạn là một lần miss phải rebuild từ DB. TTL ngắn đẩy nhiều truy vấn xuống DB.
- Bộ nhớ và tính ổn định: TTL dài giữ dữ liệu lâu, tốn RAM hơn và làm tăng nguy cơ phục vụ dữ liệu cũ; nhưng cũng giảm tải DB.
Không có một con số đúng cho mọi loại dữ liệu. Một bảng giá thay đổi mỗi vài giây cần TTL hoàn toàn khác với một bản ghi cấu hình hệ thống cả ngày mới đổi một lần. Phần còn lại của bài đi qua các loại chiến lược TTL và cách chọn cho từng tình huống.
# Cùng một traffic đọc, hai mức TTL cho ra hệ quả rất khác:
#
# TTL = 5s: key hết hạn liên tục -> nhiều miss -> DB chịu tải cao,
# nhưng dữ liệu rất tươi (stale tối đa ~5s).
#
# TTL = 3600s: key sống lâu -> rất ít miss -> DB nhàn,
# nhưng dữ liệu có thể cũ tới 1 giờ nếu không invalidate.
Hard TTL: Hết Hạn Là Xoá
Hard TTL là cơ chế hết hạn mặc định và đơn giản nhất của Redis: bạn đặt thời hạn cho key bằng EX/PX (hoặc EXPIRE), khi tới hạn Redis xoá key đi. Lần đọc kế tiếp sẽ là một cache miss và application phải rebuild giá trị từ database rồi ghi lại cache.
# Đặt hard TTL 300 giây khi ghi cache
SET user:42 "{...json...}" EX 300
# Sau 300s, GET trả về (nil) -> miss -> đọc DB -> SET lại
GET user:42
# (nil)
Ưu điểm của hard TTL:
- Đơn giản, không cần metadata thêm: chỉ là một option khi
SET. Không phải lưu timestamp hay logic phụ. - Đảm bảo độ tươi cận trên: dữ liệu trong cache không bao giờ cũ quá TTL (kể cả khi invalidation lỗi).
- Tự dọn bộ nhớ: key hết hạn được giải phóng, không cần job dọn dẹp riêng.
Nhược điểm cần lưu ý:
- Miss spike khi nhiều key hết hạn cùng lúc: nếu một loạt key được ghi gần như đồng thời với cùng TTL (ví dụ sau một lần warm-up hoặc cùng một request đông), chúng sẽ cùng hết hạn trong một khoảnh khắc. Tất cả cùng miss, cùng đập xuống DB — đây là mầm mống của cache stampede (sẽ phân tích sâu ở bài 14). Cách giảm nhẹ là TTL jitter ở mục 7.
- Khoảng trống lúc rebuild: giữa lúc key hết hạn và lúc rebuild xong, request phải chờ DB. Với dữ liệu được đọc rất nhiều thì khoảnh khắc đó có thể có nhiều request cùng chờ.
Hard TTL phù hợp với phần lớn dữ liệu cache thông thường, nơi một lần rebuild ngẫu nhiên không gây vấn đề. Khi rebuild đắt hoặc dữ liệu cực hot, ta cân nhắc soft TTL.
Soft TTL (Logical Expiry)
Soft TTL (còn gọi là logical expiry — hết hạn logic) tách rời hai khái niệm: khi nào nên làm mới dữ liệu và khi nào dữ liệu thực sự bị xoá. Thay vì để Redis xoá key, ta lưu kèm trong value một timestamp đánh dấu mốc "nên refresh sau thời điểm này". Dữ liệu vẫn còn trong cache kể cả khi đã quá soft-TTL.
Ý tưởng cốt lõi: value không chỉ chứa dữ liệu, mà còn chứa metadata về độ tươi.
# Value cache mang theo metadata soft-TTL (lưu dạng JSON)
{
"data": { "id": 42, "name": "An", "price": 199000 },
"freshUntil": 1716600000000 // mốc epoch ms: sau đây nên refresh
}
# Đặt một hard TTL DÀI hơn nhiều làm cận trên an toàn (ví dụ gấp vài lần soft-TTL)
SET product:42 "{...json...}" EX 1800 # hard cap 30 phút
Khi đọc, application so sánh freshUntil với thời điểm hiện tại:
- Nếu còn tươi (
now < freshUntil): trả dữ liệu ngay. - Nếu đã quá soft-TTL nhưng value vẫn còn: vẫn trả dữ liệu cũ ngay lập tức, đồng thời kích hoạt một tiến trình refresh nền để cập nhật value mới. Người dùng không phải chờ rebuild.
Đây chính là nền tảng của stale-while-revalidate — phục vụ bản hơi cũ trong lúc làm mới ngầm — sẽ được phân tích đầy đủ ở bài 15. Lợi ích lớn nhất là không có khoảnh khắc cache trống: vì value chưa bị xoá, request không bao giờ phải chờ DB rebuild xong, nhờ đó tránh được miss spike và stampede.
Đổi lại, soft TTL phức tạp hơn hard TTL: phải lưu metadata, phải có cơ chế refresh nền (và thường cần một lock nhỏ để chỉ một request lo việc refresh, tránh nhiều refresh trùng). Vẫn nên đặt một hard TTL dài làm cận trên để key không sống mãi nếu refresh liên tục thất bại.
Business TTL: Suy TTL Từ Nghiệp Vụ
Hard và soft TTL nói về cơ chế hết hạn. Business TTL nói về con số: chọn bao nhiêu giây cho từng loại dữ liệu dựa trên đặc tính nghiệp vụ của nó. Đây là nơi kiến thức về domain quan trọng hơn kiến thức về Redis.
Ba câu hỏi giúp suy ra TTL cho một loại dữ liệu:
- Dữ liệu thay đổi bao thường xuyên? Giá sàn giao dịch có thể đổi từng giây; hồ sơ người dùng vài ngày mới đổi; cấu hình hệ thống cả tuần mới đổi. TTL không nên dài hơn khoảng thời gian dữ liệu thường thay đổi (trừ khi có invalidation chủ động).
- Mức chấp nhận stale (tolerance staleness) là bao nhiêu? Người dùng có chịu được dữ liệu cũ vài giây không? Số dư ví điện tử cần gần như tức thời; số lượt xem một bài viết thì cũ vài phút cũng chẳng sao. Tolerance càng cao thì TTL có thể càng dài.
- Rebuild tốn kém ra sao? Một truy vấn join nặng hay tổng hợp báo cáo thì rất đắt khi rebuild; TTL nên dài hơn để giảm tần suất rebuild. Một
SELECTtheo khoá chính rất rẻ thì TTL ngắn cũng không sao.
Một cách diễn đạt trực giác:
# TTL hợp lý nằm trong vùng:
# TTL <= chu kỳ thay đổi điển hình của dữ liệu
# TTL <= mức stale mà nghiệp vụ chấp nhận
# TTL đủ lớn để chi phí rebuild trên DB không vượt ngân sách
#
# Ví dụ giá sản phẩm:
# - đổi nhanh (vài chục giây) -> TTL ngắn (30s)
# - người dùng chịu được lệch ~30s -> phù hợp
# - rebuild rẻ (1 SELECT theo id) -> không lo tải
# => chọn business TTL = 30s (kèm jitter)
Điểm mấu chốt: mỗi loại dữ liệu nên có TTL riêng được suy ra có chủ đích, thay vì một con số dùng chung cho tất cả. Tổ chức TTL thành các hằng số đặt tên rõ ràng (ví dụ TTL_PRICE, TTL_PROFILE, TTL_CONFIG) để dễ đọc và dễ chỉnh.
Bảng Gợi Ý TTL Theo Loại Dữ Liệu
Bảng dưới là điểm khởi đầu, không phải con số tuyệt đối — luôn điều chỉnh theo nghiệp vụ thực tế của bạn. Cột TTL gợi ý là giá trị base (chưa cộng jitter).
| Loại dữ liệu | TTL gợi ý (base) | Lý do |
|---|---|---|
| Giá sản phẩm, tỉ giá, tồn kho | 15 – 60 giây | Thay đổi rất nhanh, nghiệp vụ cần gần với thời gian thực; rebuild thường rẻ. |
| Kết quả tìm kiếm, feed cá nhân hoá | 1 – 5 phút | Đổi vừa phải, người dùng chịu được lệch ngắn; rebuild khá đắt nên không nên quá ngắn. |
| Hồ sơ người dùng (profile) | 30 phút – 1 giờ | Ít khi đổi trong một phiên; thường có invalidation khi user tự sửa nên TTL chỉ là lưới an toàn. |
| Danh mục, thông tin sản phẩm tĩnh | 1 – 6 giờ | Đổi theo đợt (nhập hàng, cập nhật mô tả); tolerance staleness cao. |
| Cấu hình hệ thống, feature flag | 6 – 24 giờ | Rất ít đổi; nên kèm invalidation chủ động khi thay đổi để cập nhật ngay thay vì chờ TTL. |
| Dữ liệu tham chiếu gần như bất biến (mã quốc gia, danh mục cố định) | 12 – 24 giờ | Gần như không đổi; TTL dài để giảm tối đa tải DB, chỉ làm mới định kỳ. |
| Session / token tạm | Theo vòng đời nghiệp vụ (ví dụ 15 – 30 phút, trượt theo hoạt động) | TTL ở đây mang ý nghĩa nghiệp vụ (thời hạn phiên), không chỉ là tối ưu cache. |
Lưu ý: với những loại có invalidation chủ động (profile, config), TTL đóng vai trò lưới an toàn phòng khi invalidation lỗi, nên có thể đặt dài. Với loại không invalidate được (giá lấy từ nguồn ngoài), TTL chính là công cụ kiểm soát độ tươi duy nhất, nên đặt theo tolerance staleness.
TTL Jitter: Chống Mass-Expiration
Vấn đề: nếu nhiều key được ghi gần như cùng thời điểm với cùng một TTL cố định, chúng sẽ cùng hết hạn trong một khoảnh khắc. Tất cả cùng miss, hàng loạt request cùng đập xuống DB rebuild — gọi là mass-expiration, một dạng cache stampede. Tình huống này hay xảy ra sau khi warm-up cache, sau deploy, hoặc khi một burst traffic ghi nhiều key một lúc.
Giải pháp đơn giản và hiệu quả là TTL jitter: random hoá TTL một chút quanh giá trị base để các key hết hạn rải đều theo thời gian thay vì dồn vào một điểm.
# Công thức:
TTL = base + random(0 .. jitter)
# Ví dụ base = 300s, jitter = 60s
# mỗi key nhận TTL ngẫu nhiên trong khoảng [300, 360] giây
# -> điểm hết hạn trải đều trên 60 giây thay vì dồn vào 1 thời điểm
Vài lưu ý khi chọn jitter:
- Độ lớn jitter nên đáng kể so với base để rải đủ rộng — thường 10% đến 25% của base là hợp lý. Jitter quá nhỏ thì các key vẫn dồn cụm.
- Có thể jitter hai chiều (
base ± jitter) thay vì chỉ cộng, miễn TTL không xuống dưới một ngưỡng tối thiểu hợp lý. - Jitter giảm nhẹ stampede chứ không loại bỏ hoàn toàn. Với dữ liệu cực hot và rebuild đắt, kết hợp thêm lock hoặc soft TTL (stale-while-revalidate). Chủ đề chống stampede đầy đủ ở bài 14.
Quy tắc thực hành: mặc định luôn thêm jitter cho TTL của cache, đặc biệt khi cache được ghi theo từng burst lớn. Chi phí gần như bằng không nhưng tránh được nhiều sự cố tải đột biến.
Code ioredis (TypeScript)
Triển khai với ioredis: định nghĩa business TTL theo từng loại, hàm tính jitter, set hard TTL, và set kèm metadata soft-TTL. Đơn vị TTL dùng giây cho option EX.
import Redis from "ioredis";
const redis = new Redis({ host: "127.0.0.1", port: 6379 });
// 1. Business TTL: mỗi loại dữ liệu một hằng số (giây)
const TTL = {
PRICE: 30, // giá đổi nhanh
PROFILE: 3600, // hồ sơ ít đổi
CONFIG: 86400, // cấu hình rất ít đổi
} as const;
// 2. TTL jitter: TTL = base + random(0..jitter)
// jitterRatio mặc định 0.2 (tức 20% của base)
function withJitter(base: number, jitterRatio = 0.2): number {
const jitter = Math.floor(base * jitterRatio);
return base + Math.floor(Math.random() * (jitter + 1));
}
// 3. Hard TTL: hết hạn là Redis xoá, lần sau rebuild từ DB
async function setHard<T>(key: string, value: T, baseTtl: number): Promise<void> {
const ttl = withJitter(baseTtl);
await redis.set(key, JSON.stringify(value), "EX", ttl);
}
// 4. Soft TTL (logical expiry): lưu kèm mốc nên refresh.
// hardCap là hard TTL dài làm cận trên an toàn (mặc định gấp 4 lần soft).
interface SoftEnvelope<T> {
data: T;
freshUntil: number; // epoch ms
}
async function setSoft<T>(
key: string,
value: T,
softTtlSec: number,
hardCapSec = softTtlSec * 4,
): Promise<void> {
const envelope: SoftEnvelope<T> = {
data: value,
freshUntil: Date.now() + withJitter(softTtlSec) * 1000,
};
await redis.set(key, JSON.stringify(envelope), "EX", withJitter(hardCapSec));
}
// 5. Đọc soft TTL: còn tươi -> trả ngay; quá soft-TTL nhưng còn value
// -> trả bản cũ và đánh dấu cần refresh nền (nền tảng stale-while-revalidate).
async function getSoft<T>(key: string): Promise<{ data: T; stale: boolean } | null> {
const raw = await redis.get(key);
if (raw === null) return null; // hết cả hard cap -> miss thật sự
const env = JSON.parse(raw) as SoftEnvelope<T>;
const stale = Date.now() >= env.freshUntil;
return { data: env.data, stale };
}
// Ví dụ dùng:
// await setHard("price:42", { price: 199000 }, TTL.PRICE);
// await setSoft("product:42", { id: 42, name: "An" }, TTL.PROFILE);
Lưu ý: với soft TTL, sau khi getSoft trả stale = true, application chịu trách nhiệm gọi rebuild nền (thường kèm một lock ngắn để chỉ một request làm việc đó). Cơ chế lock và refresh nền đầy đủ thuộc về bài 14 và 15.
Code redis-py (Python)
Phiên bản tương đương bằng redis-py. Đặt decode_responses=True để nhận str, set TTL qua tham số ex của set.
import json
import random
import time
import redis
r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
# 1. Business TTL theo từng loại (giây)
TTL_PRICE = 30 # giá đổi nhanh
TTL_PROFILE = 3600 # hồ sơ ít đổi
TTL_CONFIG = 86400 # cấu hình rất ít đổi
# 2. TTL jitter: TTL = base + random(0..jitter)
def with_jitter(base: int, jitter_ratio: float = 0.2) -> int:
jitter = int(base * jitter_ratio)
return base + random.randint(0, jitter)
# 3. Hard TTL: hết hạn là Redis xoá, lần sau rebuild từ DB
def set_hard(key: str, value, base_ttl: int) -> None:
r.set(key, json.dumps(value), ex=with_jitter(base_ttl))
# 4. Soft TTL (logical expiry): lưu kèm mốc nên refresh.
# hard_cap là hard TTL dài làm cận trên an toàn.
def set_soft(key: str, value, soft_ttl: int, hard_cap: int | None = None) -> None:
if hard_cap is None:
hard_cap = soft_ttl * 4
envelope = {
"data": value,
# freshUntil tính bằng epoch milli-giây cho đồng nhất với bản TS
"freshUntil": int(time.time() * 1000) + with_jitter(soft_ttl) * 1000,
}
r.set(key, json.dumps(envelope), ex=with_jitter(hard_cap))
# 5. Đọc soft TTL: trả (data, stale). stale=True nghĩa là quá soft-TTL,
# application nên trả bản cũ ngay rồi refresh nền.
def get_soft(key: str):
raw = r.get(key)
if raw is None:
return None # hết cả hard cap -> miss thật sự
env = json.loads(raw)
stale = int(time.time() * 1000) >= env["freshUntil"]
return {"data": env["data"], "stale": stale}
# Ví dụ dùng:
# set_hard("price:42", {"price": 199000}, TTL_PRICE)
# set_soft("product:42", {"id": 42, "name": "An"}, TTL_PROFILE)
Hai bản code cùng một cấu trúc: hằng số business TTL theo loại, hàm with_jitter dùng chung công thức base + random(0..jitter), hard TTL qua option set TTL, và soft TTL bằng cách bọc value trong envelope kèm freshUntil cùng một hard cap dài làm cận trên.
Trade-off Khi Chọn TTL
Mọi quyết định TTL đều rơi vào một dải giữa hai cực, không có lựa chọn miễn phí:
- TTL ngắn: dữ liệu tươi hơn, stale window hẹp; nhưng key hết hạn thường xuyên nên nhiều miss hơn, đẩy nhiều truy vấn xuống DB và tăng nguy cơ stampede. Hit ratio thường thấp hơn.
- TTL dài: ít miss, DB nhàn, hit ratio cao; nhưng dữ liệu có thể stale lâu hơn (nếu không có invalidation) và tốn RAM hơn vì key sống lâu, kể cả các key ít được đọc lại.
Một vài hệ quả thực tế đáng cân nhắc:
- Hard TTL vs soft TTL: hard đơn giản nhưng có khoảnh khắc cache trống lúc rebuild; soft loại bỏ khoảnh khắc đó (trả bản cũ trong khi refresh) nhưng phức tạp hơn và phải chấp nhận phục vụ dữ liệu hơi cũ một cách có chủ đích.
- Jitter vs độ tươi đều nhau: jitter làm thời điểm hết hạn không còn cố định, nên stale window của các key không đồng nhất tuyệt đối. Đây là cái giá rẻ và đáng đánh đổi để tránh mass-expiration.
- TTL theo loại vs một TTL chung: tách TTL theo loại tốn công cấu hình và bảo trì, nhưng tối ưu hơn nhiều so với một con số dùng chung vốn luôn sai cho ít nhất một loại dữ liệu.
Cách tiếp cận thực dụng: bắt đầu từ business TTL suy ra theo nghiệp vụ (mục 5), luôn thêm jitter (mục 7), đo hit ratio và tải DB thực tế, rồi tinh chỉnh. Với dữ liệu cực hot và rebuild đắt, nâng cấp lên soft TTL.
Pitfalls & Anti-patterns
- Một TTL cố định cho mọi loại dữ liệu: đặt chung kiểu
EX 3600cho cả giá (đổi từng giây) lẫn cấu hình (cả ngày mới đổi). Con số chung này luôn sai cho ít nhất một loại: hoặc giá quá stale, hoặc config bị làm mới vô ích. Hãy suy business TTL theo từng loại. - Không jitter, key cùng hết hạn đồng loạt: ghi một loạt key với cùng TTL cố định (sau warm-up, sau deploy) khiến chúng hết hạn cùng lúc, tạo miss spike và stampede đập xuống DB. Luôn thêm
random(0..jitter). - TTL quá dài cho dữ liệu đổi nhanh: cache giá hay tồn kho với TTL hàng giờ làm người dùng thấy số liệu sai lệch nghiêm trọng. TTL không nên dài hơn chu kỳ thay đổi điển hình của dữ liệu (trừ khi có invalidation chủ động đáng tin cậy).
- Quên TTL hoàn toàn (memory leak):
SETmà thiếuEX/PXtạo key vĩnh viễn. Cache phình to dần, có thể chạmmaxmemoryvà kích hoạt eviction ngoài ý muốn, hoặc giữ dữ liệu stale mãi mãi. Luôn đặt TTL mặc định cho mọi key cache. - TTL quá ngắn vô tình triệt tiêu lợi ích cache: đặt TTL vài giây cho dữ liệu rebuild đắt khiến gần như mọi request đều miss và đập DB; cache lúc này chỉ thêm chi phí GET/SET mà không giảm tải. Cân nhắc rebuild cost khi chọn TTL.
- Soft TTL nhưng không đặt hard cap: dùng logical expiry mà không kèm một hard TTL dài làm cận trên; nếu refresh nền liên tục thất bại, key sẽ phục vụ dữ liệu cũ vô thời hạn. Luôn có hard cap an toàn.
- Lẫn lộn đơn vị giây và mili-giây: nhầm
EX(giây) vớiPX(mili-giây) khiến TTL lệch 1000 lần — đặt 300 mà tưởng 300 giây trong khi thực ra 300 mili-giây, hoặc ngược lại. Thống nhất đơn vị và kiểm tra kỹ.
Tổng Kết & Quiz
Tổng kết
- TTL là quyết định chiến lược cân bằng độ tươi, tải DB và bộ nhớ — không có một con số đúng cho mọi loại dữ liệu.
- Hard TTL: Redis xoá key khi hết hạn, lần đọc sau rebuild từ DB. Đơn giản nhưng dễ gây miss spike khi nhiều key hết hạn cùng lúc.
- Soft TTL (logical expiry): lưu kèm timestamp nên refresh; value vẫn còn sau soft-TTL, trả bản cũ trong khi làm mới nền — nền tảng của stale-while-revalidate (bài 15). Luôn kèm hard cap.
- Business TTL: suy ra từ tần suất thay đổi, tolerance staleness và chi phí rebuild; mỗi loại dữ liệu một TTL riêng.
- TTL jitter:
TTL = base + random(0..jitter)để rải thời điểm hết hạn, chống mass-expiration và stampede (deep ở bài 14). - Trade-off: TTL ngắn thì tươi nhưng nhiều miss và tải DB; TTL dài thì ít miss nhưng stale và tốn RAM.
Quiz 5 câu
- Phân biệt hard TTL và soft TTL (logical expiry) về cơ chế hết hạn và hành vi khi quá hạn. Soft TTL là nền tảng cho kỹ thuật nào?
- Nêu ba yếu tố dùng để suy ra business TTL cho một loại dữ liệu và mối quan hệ của chúng với độ dài TTL.
- Viết công thức TTL jitter và giải thích vì sao nó giúp chống mass-expiration. Jitter có loại bỏ hoàn toàn cache stampede không?
- Vì sao dùng một TTL cố định cho mọi loại dữ liệu là một anti-pattern? Cho ví dụ một hậu quả cụ thể.
- Khi dùng soft TTL, vì sao vẫn cần đặt một hard TTL dài làm cận trên (hard cap)? Điều gì xảy ra nếu thiếu nó?
Đáp án gợi ý
- Hard TTL: Redis tự xoá key khi hết hạn, quá hạn là cache miss và phải rebuild từ DB. Soft TTL: lưu kèm timestamp
freshUntil; khi quá soft-TTL value vẫn còn nên application trả bản cũ ngay và refresh nền (key chỉ thực sự mất khi hết hard cap). Soft TTL là nền tảng cho stale-while-revalidate. - (1) Tần suất thay đổi của dữ liệu — TTL không nên dài hơn chu kỳ thay đổi điển hình. (2) Tolerance staleness — chấp nhận stale càng cao thì TTL càng dài được. (3) Chi phí rebuild trên DB — rebuild càng đắt thì TTL nên dài hơn để giảm tần suất rebuild.
TTL = base + random(0..jitter). Nó làm các key được ghi cùng lúc nhận TTL khác nhau nên hết hạn rải đều theo thời gian thay vì dồn vào một điểm, giảm miss spike. Jitter chỉ giảm nhẹ chứ không loại bỏ hoàn toàn stampede; với dữ liệu cực hot cần kết hợp lock hoặc soft TTL.- Vì các loại dữ liệu có đặc tính khác nhau (giá đổi từng giây, config cả ngày mới đổi), một con số chung luôn sai cho ít nhất một loại. Ví dụ: TTL 3600s áp cho giá khiến người dùng thấy giá cũ tới 1 giờ; hoặc TTL 30s áp cho config khiến config bị rebuild vô ích quá thường xuyên.
- Vì refresh nền có thể liên tục thất bại (DB lỗi, code refresh hỏng); nếu không có hard cap, key sẽ phục vụ dữ liệu cũ vô thời hạn và không bao giờ tự xoá. Hard cap đảm bảo cận trên cho stale window và giải phóng key cuối cùng.
Bài tiếp theo
Bài 13 bàn về Negative Caching: cache có kiểm soát cho các kết quả "không tồn tại" (ví dụ GET user:999 mà DB không có) để chặn các request lặp lại đập xuống DB tìm thứ không có, cùng cách đặt TTL ngắn riêng cho negative entry và tránh khoá trạng thái lỗi.
