Mục lục
- Mục Tiêu Bài Học
- Vì Sao Thiết Kế Key Quan Trọng
- Naming Convention & Quy Ước Ký Tự
- Namespace: Tách Theo Service & Môi Trường
- Versioning Key Để Invalidate Hàng Loạt
- Đưa Tham Số Vào Key & Hash Khi Quá Dài
- Key Length, Memory & Keyspace Có Kiểm Soát
- Code Build Cache Key Với ioredis (TypeScript)
- Code Build Cache Key Với redis-py (Python)
- Pitfalls & Anti-patterns
- Tổng Kết & Quiz
Mục Tiêu Bài Học
- Hiểu vì sao thiết kế cache key là một quyết định kiến trúc, không chỉ là chuyện đặt tên: nó ảnh hưởng tới collision, debug, invalidation, big key và việc kiểm soát keyspace.
- Nắm naming convention chuẩn của cộng đồng Redis: dùng dấu
:phân cấp, prefix theo domain hoặc service, viết lowercase, tránh space và ký tự đặc biệt. - Biết cách dùng namespace tách theo app, service và môi trường để nhiều thành phần dùng chung một Redis instance mà không đụng key của nhau.
- Áp dụng versioning vào key để invalidate hàng loạt khi đổi format hoặc schema của dữ liệu cache.
- Biết khi nào và làm sao đưa các tham số ảnh hưởng kết quả (query params, locale, page) vào key, và khi nào nên hash bằng SHA-1/MD5 cho gọn.
- Hiểu chi phí RAM của mỗi key, cân bằng giữa readable và ngắn gọn, và tránh tạo keyspace vô hạn từ input tự do.
- Viết được hàm build cache key có namespace, version và hash query bằng cả ioredis (TypeScript) và redis-py (Python).
Vì Sao Thiết Kế Key Quan Trọng
Redis là một kho key-value phẳng: toàn bộ dữ liệu nằm trong một keyspace duy nhất, không có "thư mục" hay "bảng" thật sự. Cấu trúc duy nhất mà bạn có chính là cách đặt tên key. Vì thế thiết kế key không phải chuyện thẩm mỹ, mà là quyết định ảnh hưởng trực tiếp tới vận hành. Một bộ key được thiết kế tốt giúp:
- Tránh collision (đụng key): hai phần khác nhau của hệ thống vô tình dùng cùng một tên key sẽ ghi đè lên nhau, gây bug rất khó tìm. Ví dụ cả module người dùng và module sản phẩm cùng dùng key
123thì một bên sẽ đọc nhầm dữ liệu của bên kia. - Dễ debug: khi production có sự cố, bạn cần nhìn một key và đoán ngay nó chứa gì, thuộc service nào, version nào.
prod:user:v2:123:profiletự nói lên ý nghĩa; còna1b2c3thì vô nghĩa. - Dễ invalidate: khi cần xoá hoặc làm mới một nhóm dữ liệu, cấu trúc key rõ ràng cho phép nhắm đúng nhóm (theo prefix, theo version) thay vì dò từng key.
- Tránh big key & kiểm soát keyspace: thiết kế key cũng đi đôi với thiết kế giá trị. Một tên key phản ánh đúng mức chi tiết của dữ liệu giúp bạn không gom quá nhiều thứ vào một key (big key), và không sinh ra vô số key rác.
Hậu quả của thiết kế kém thường không xuất hiện ngay khi mới làm, mà bộc lộ khi hệ thống lớn lên: keyspace phình to khó dọn, không thể invalidate theo nhóm, đụng key giữa các service, và mất hàng giờ debug chỉ vì một tên key mơ hồ.
# Hai cách đặt tên cho cùng một dữ liệu — khác biệt khi vận hành
# (xấu) phẳng, mơ hồ, không namespace, không version
123
user_123
profile123
# (tốt) phân cấp, có namespace + version, đọc là hiểu
prod:user:v2:123:profile
prod:order:v1:9981:summary
Naming Convention & Quy Ước Ký Tự
Về mặt kỹ thuật, key trong Redis là một chuỗi binary-safe: gần như mọi byte đều hợp lệ. Nhưng "hợp lệ" không có nghĩa là "nên dùng". Cộng đồng Redis đã hội tụ về một bộ quy ước giúp key vừa máy đọc được vừa người đọc được:
- Dùng dấu hai chấm
:để phân cấp. Đây là quy ước phổ biến nhất, ví dụuser:123:profile. Dấu:tạo cảm giác phân cấp (dù keyspace thực ra vẫn phẳng) và là dấu phân tách mà nhiều công cụ GUI như RedisInsight tự nhận diện để gom key thành dạng cây. - Prefix theo domain hoặc service. Phần đầu key nên là tên thực thể nghiệp vụ:
user:,order:,product:,session:. Nhìn prefix là biết key thuộc nhóm nào. - Viết lowercase và thống nhất quy tắc. Key phân biệt hoa thường (
User:1khácuser:1), nên trộn hoa thường sẽ gây miss ngầm. Chọn lowercase cho toàn bộ. - Tránh space và ký tự đặc biệt. Khoảng trắng, ký tự glob như
*?[], hoặc ký tự cần escape trên dòng lệnh khiến key khó dùng vớiredis-cli,SCAN MATCH, và dễ gây lỗi khi build chuỗi. Hạn chế tập ký tự ở chữ thường, số, dấu:, dấu-hoặc_. - Thống nhất một định dạng cho cả team và ghi lại thành tài liệu. Quan trọng không phải bạn chọn
:hay-làm separator phụ, mà là cả hệ thống dùng chung một quy ước.
Mẫu key tổng quát thường thấy trong production:
# <namespace>:<domain>:<version>:<id>:<thuộc-tính>
prod:user:v2:123:profile
prod:user:v2:123:settings
prod:product:v1:559:detail
prod:cart:v1:abf30c:items
Lưu ý: : chỉ là quy ước, Redis không hiểu nó như cấp bậc thật. Mọi thao tác (GET, DEL, SCAN) vẫn làm việc trên chuỗi key đầy đủ. Việc "gom theo prefix" mà bạn thấy trên GUI chỉ là cách hiển thị, không phải tính năng lưu trữ.
Namespace: Tách Theo Service & Môi Trường
Trong thực tế, một Redis instance (hoặc cluster) thường được nhiều service và nhiều môi trường dùng chung để tiết kiệm hạ tầng. Nếu không có namespace, các thành phần này sẽ giẫm chân nhau. Namespace là tiền tố đặt ở đầu key để cô lập keyspace của từng nhóm.
- Tách theo môi trường:
prod:,staging:,dev:. Nhờ vậy một lần flush nhầm ở staging không đụng dữ liệu prod, và bạn không bao giờ đọc nhầm cache của môi trường khác. - Tách theo service hoặc app:
billing:,search:,auth:. Mỗi service sở hữu namespace riêng, dễ phân quyền, dễ tính dung lượng theo service. - Kết hợp cả hai:
prod:billing:invoice:v1:8842. Đọc từ trái sang phải là biết ngay: môi trường prod, service billing, thực thể invoice, version 1, id 8842.
Về cách hiện thực namespace, có hai hướng:
- Prefix trong tên key (khuyến nghị): đưa namespace vào ngay phần đầu key như trên. Đây là cách linh hoạt nhất, hoạt động với mọi client và cả Redis Cluster.
- Tách database bằng số DB (
SELECT 0..15): Redis có 16 logical database mặc định. Tuy nhiên cách này không được khuyến khích để phân tách service vì: số DB giới hạn và khó mở rộng, Redis Cluster chỉ hỗ trợ DB 0, và nhiều thư viện/connection pool xử lýSELECTkhông thuận tiện. Dùng prefix trong key bền vững hơn.
Một mẹo hữu ích với ioredis là option keyPrefix: client tự động thêm tiền tố vào mọi key, giúp gắn namespace mà không phải sửa từng lời gọi (sẽ minh hoạ ở phần code).
# Cùng một Redis instance, nhưng các namespace không đụng nhau
prod:auth:session:abf30c
prod:billing:invoice:v1:8842
staging:auth:session:abf30c # khác môi trường, không lẫn với prod
Versioning Key Để Invalidate Hàng Loạt
Một ngày bạn đổi format dữ liệu cache: thêm trường mới, đổi kiểu serialize từ JSON sang một schema khác, hoặc sửa cách tính một giá trị. Lúc này toàn bộ cache cũ đã sai format và cần biến mất. Nếu key không có version, bạn buộc phải đi tìm và xoá từng nhóm key cũ — chậm và rủi ro.
Versioning key giải quyết bằng cách nhúng một số version vào tên key, ví dụ user:v2:123. Khi đổi format, bạn chỉ cần tăng version trong code (từ v2 lên v3). Kết quả:
- Toàn bộ key mới được sinh ra với prefix
user:v3:, không trùng vớiuser:v2:. Ứng dụng tự động miss và nạp lại theo format mới. - Các key
v2cũ không còn được đọc nữa; chúng sẽ tự hết hạn theo TTL hoặc bị eviction dọn dần. Bạn đã invalidate hàng loạt chỉ bằng một thay đổi hằng số, không cần lệnh xoá nào. - Quá trình deploy an toàn hơn: trong lúc rollout, instance cũ vẫn đọc
v2, instance mới ghiv3, không xung đột. Nếu cần rollback, bản cũ vẫn còn cachev2đang sống.
Đặt version ở đâu trong key? Thường ngay sau domain: <ns>:<domain>:v<n>:<id>. Đặt như vậy giúp version áp dụng cho cả nhóm thực thể đó. Nếu chỉ một loại dữ liệu đổi format, có thể version riêng cho loại đó thay vì version chung toàn hệ thống.
# Trước khi đổi format (CACHE_VERSION = v2)
prod:user:v2:123:profile
# Sau khi đổi format, chỉ cần tăng hằng số (CACHE_VERSION = v3)
prod:user:v3:123:profile # key mới, ứng dụng miss rồi nạp lại đúng format
# prod:user:v2:* không ai đọc nữa -> tự hết hạn theo TTL / bị eviction
Versioning là một dạng invalidation "lười" và an toàn: thay vì chủ động xoá, bạn cho cache cũ trở nên vô hình. Các kỹ thuật invalidation chủ động (xoá theo tag, theo event) sẽ được phân tích ở bài 17.
Đưa Tham Số Vào Key & Hash Khi Quá Dài
Nguyên tắc cốt lõi của một cache key đúng: mọi yếu tố làm thay đổi kết quả phải nằm trong key. Nếu hai request cho ra hai kết quả khác nhau nhưng dùng chung một key, cache sẽ trả nhầm dữ liệu của request này cho request kia.
Ví dụ một endpoint danh sách sản phẩm có thể phụ thuộc vào: bộ lọc (category, price), locale (ngôn ngữ hiển thị), phân trang (page, limit), thứ tự sắp xếp (sort). Tất cả những tham số này phải đi vào key:
# Sai: bỏ qua page và locale -> trang 2 đọc nhầm cache của trang 1,
# người dùng vi đọc nhầm dữ liệu en
prod:product:v1:list
# Đúng: mọi tham số ảnh hưởng kết quả đều có trong key
prod:product:v1:list:cat=phone:sort=price_asc:locale=vi:page=2:limit=20
Khi số tham số nhiều, key trở nên dài và khó đọc, thậm chí vượt giới hạn thực tế bạn muốn cho key. Lúc này dùng hash (SHA-1 hoặc MD5) trên phần tham số để rút gọn thành một chuỗi cố định, vẫn giữ namespace và version ở dạng readable phía trước:
# Hash phần tham số để key gọn và độ dài cố định
# vẫn giữ prefix readable để debug biết đây là cache list sản phẩm
prod:product:v1:list:9c1185a5c5e9fc54612808977ee8f548b2258d31
Một vài lưu ý quan trọng khi hash:
- Chuẩn hoá (canonicalize) tham số trước khi hash. Phải sort key của tham số để
a=1&b=2vàb=2&a=1ra cùng một hash; nếu không, hai request giống hệt về ngữ nghĩa lại sinh hai key khác nhau, làm giảm hit ratio. - SHA-1/MD5 ở đây chỉ để rút gọn, không phải để bảo mật. Va chạm hash trên không gian tham số thực tế là cực kỳ hiếm; nếu cực kỳ lo ngại có thể dùng SHA-256, nhưng đa số trường hợp SHA-1 là đủ và ngắn gọn.
- Giữ phần readable phía trước (namespace, domain, version, loại endpoint) để khi debug vẫn biết key thuộc về cái gì, chỉ phần biến thiên mới bị hash.
- Hash là một chiều: bạn không tái tạo lại tham số từ key. Nếu cần tra ngược, hãy log mapping hoặc lưu kèm metadata.
Key Length, Memory & Keyspace Có Kiểm Soát
Về mặt giới hạn cứng, một key trong Redis có thể dài tối đa 512 MB (key cũng là một String). Nhưng giới hạn này hoàn toàn không liên quan tới thực hành tốt: trong production key chỉ nên dài cỡ vài chục ký tự. Lý do là mỗi key đều tốn RAM cho chính tên của nó, cộng với overhead của cấu trúc nội bộ (entry trong dict, con trỏ, metadata). Khi bạn có hàng chục triệu key, vài chục byte thừa trên mỗi tên key cộng lại thành hàng trăm MB lãng phí.
- Cân bằng readable và ngắn gọn. Key quá ngắn (
u:1) tiết kiệm RAM nhưng khó debug; key quá dài (nhồi cả mô tả vào tên) tốn RAM và rối. Mức hợp lý: đủ prefix để hiểu (namespace, domain, version) và phần định danh ngắn gọn. Với phần biến thiên dài thì hash như mục trên. - Dùng id số hoặc id ngắn thay vì chuỗi dài cho phần định danh khi có thể.
- Dùng
MEMORY USAGE keyđể đo dung lượng thực một key (gồm cả tên và giá trị), giúp phát hiện big key và ước lượng chi phí.
Vấn đề lớn hơn độ dài là keyspace vô hạn không kiểm soát. Nếu bạn sinh key trực tiếp từ input tự do của người dùng mà không giới hạn, kẻ tấn công (hoặc chỉ là traffic bất thường) có thể tạo ra vô số key duy nhất, mỗi key một lần dùng rồi bỏ. Hậu quả là cache phình to vô tận — một dạng memory leak qua cache:
# Nguy hiểm: key sinh trực tiếp từ chuỗi tìm kiếm tự do của user
prod:search:v1:"áo thun nam size XXL màu xanh rẻ nhất hà nội ..."
# Mỗi truy vấn lạ tạo 1 key mới, hầu như không bao giờ được đọc lại
# -> keyspace phình to, RAM cạn, eviction đá cả hot data
Cách phòng tránh: luôn đặt TTL cho mọi key (để key rác tự chết), hash hoặc chuẩn hoá input để giới hạn không gian, chỉ cache những truy vấn phổ biến (đo trước), và cấu hình maxmemory + eviction policy hợp lý (xem bài tối ưu hiệu năng). Hãy luôn tự hỏi: "Key này có bị chặn trên về số lượng không?" Nếu câu trả lời là không, đó là một rủi ro.
Code Build Cache Key Với ioredis (TypeScript)
Đừng rải chuỗi key khắp codebase. Hãy gom việc build key vào một chỗ duy nhất: một hàm có namespace, version và hash query. Điều này giúp đổi version chỉ ở một nơi, và đảm bảo mọi nơi sinh key đúng quy ước.
import Redis from "ioredis";
import { createHash } from "node:crypto";
// keyPrefix giúp gắn namespace môi trường tự động cho MỌI key
const redis = new Redis({
host: "127.0.0.1",
port: 6379,
keyPrefix: `${process.env.APP_ENV ?? "dev"}:`, // ví dụ "prod:"
});
// Đổi version ở đây để invalidate hàng loạt khi đổi format cache
const CACHE_VERSION = "v2";
const SEP = ":";
// Chuẩn hoá + hash phần tham số (sort key để ổn định thứ tự)
function hashParams(params: Record<string, unknown>): string {
const canonical = Object.keys(params)
.sort()
.map((k) => `${k}=${String(params[k])}`)
.join("&");
return createHash("sha1").update(canonical).digest("hex");
}
// Build key cho một entity đơn (vd user:v2:123:profile)
export function entityKey(
domain: string,
id: string | number,
attr?: string,
): string {
// Lưu ý: keyPrefix của ioredis sẽ tự thêm "prod:" phía trước
const parts = [domain, CACHE_VERSION, String(id)];
if (attr) parts.push(attr);
return parts.join(SEP);
}
// Build key cho truy vấn nhiều tham số: phần readable + hash params
export function queryKey(
domain: string,
action: string,
params: Record<string, unknown>,
): string {
return [domain, CACHE_VERSION, action, hashParams(params)].join(SEP);
}
// Ví dụ sử dụng
const k1 = entityKey("user", 123, "profile");
// "user:v2:123:profile" (đầy đủ là "prod:user:v2:123:profile")
const k2 = queryKey("product", "list", {
category: "phone",
sort: "price_asc",
locale: "vi",
page: 2,
limit: 20,
});
// "product:v2:list:<sha1 của params đã sort>"
await redis.set(k1, JSON.stringify({ id: 123 }), "EX", 300);
Vì dùng keyPrefix, mọi lệnh get/set/del của client này tự động mang namespace môi trường. Lưu ý nhỏ: khi dùng SCAN hoặc lệnh chạy raw, một số đường đi của ioredis không tự thêm prefix — hãy kiểm tra kỹ nếu bạn quét key trực tiếp.
Code Build Cache Key Với redis-py (Python)
Phiên bản tương đương bằng redis-py. Ở đây ta đưa namespace môi trường vào ngay trong hàm build key (redis-py không có option keyPrefix tích hợp như ioredis), giữ chung một nguồn duy nhất sinh key.
import hashlib
import json
import os
import redis
r = redis.Redis(host="127.0.0.1", port=6379, decode_responses=True)
# Namespace môi trường: prod / staging / dev
APP_ENV = os.getenv("APP_ENV", "dev")
# Đổi version ở đây để invalidate hàng loạt khi đổi format cache
CACHE_VERSION = "v2"
SEP = ":"
def _hash_params(params: dict) -> str:
# Chuẩn hoá: sort key để thứ tự tham số không ảnh hưởng hash
canonical = "&".join(f"{k}={params[k]}" for k in sorted(params))
return hashlib.sha1(canonical.encode("utf-8")).hexdigest()
def entity_key(domain: str, entity_id, attr: str | None = None) -> str:
parts = [APP_ENV, domain, CACHE_VERSION, str(entity_id)]
if attr:
parts.append(attr)
return SEP.join(parts)
def query_key(domain: str, action: str, params: dict) -> str:
parts = [APP_ENV, domain, CACHE_VERSION, action, _hash_params(params)]
return SEP.join(parts)
# Ví dụ sử dụng
k1 = entity_key("user", 123, "profile")
# "prod:user:v2:123:profile"
k2 = query_key(
"product",
"list",
{"category": "phone", "sort": "price_asc", "locale": "vi", "page": 2, "limit": 20},
)
# "prod:product:v2:list:<sha1 của params đã sort>"
r.set(k1, json.dumps({"id": 123}), ex=300)
Hai bản code (TS và Python) cùng một ý tưởng: namespace + domain + version + (id hoặc hash params), gom vào một hàm duy nhất. Khi cần đổi format dữ liệu, chỉ sửa CACHE_VERSION ở một nơi là cả hệ thống chuyển sang key version mới một cách an toàn.
Pitfalls & Anti-patterns
- Key không có namespace: nhiều service/môi trường dùng chung Redis với key trần như
123hayuser_1sẽ đụng nhau và ghi đè dữ liệu của nhau. Luôn thêm tiền tố môi trường + service. - Không versioning: khi đổi format dữ liệu cache mà key không có version, bạn không thể invalidate sạch bản cũ; ứng dụng đọc trúng key cũ và parse sai format. Nhúng
v<n>vào key ngay từ đầu. - Nhồi data vào tên key: nhét cả nội dung dữ liệu (tên đầy đủ, mô tả, payload) vào tên key thay vì vào giá trị. Tên key chỉ nên là định danh đủ để định vị; data thuộc về value.
- Key sinh từ user input không sanitize: lấy thẳng chuỗi người dùng nhập làm key gây hai vấn đề: ký tự đặc biệt/space làm hỏng pattern và lệnh, và keyspace phình vô hạn (memory leak). Hãy chuẩn hoá, hash, và đặt TTL.
- Bỏ tham số ảnh hưởng kết quả ra khỏi key: quên
page,locale,sort... khiến cache trả nhầm dữ liệu giữa các request khác nhau. Mọi yếu tố làm đổi kết quả phải có trong key. - Hash mà không chuẩn hoá tham số: hash trực tiếp query string chưa sort khiến
a=1&b=2vàb=2&a=1ra hai key khác nhau cho cùng một kết quả, làm tụt hit ratio. Luôn sort key trước khi hash. - Dùng
KEYS prefix:*để tìm/xoá key:KEYSquét toàn bộ keyspace và chặn server (Redis đơn luồng cho lệnh) — rất nguy hiểm trên production lớn. Thay bằngSCAN(lặp theo cursor, không chặn), hoặc tốt hơn là chủ động gom các key liên quan vào một Set / dùng tag để xoá có chủ đích thay vì quét theo pattern. - Trộn hoa thường hoặc separator không nhất quán:
User:1vàuser:1là hai key khác nhau, gây miss ngầm. Chốt một quy ước (lowercase, dùng:phân cấp) và áp dụng qua một hàm build key chung.
Tổng Kết & Quiz
Tổng kết
- Keyspace của Redis phẳng; cách đặt tên key là cấu trúc duy nhất bạn có. Thiết kế key tốt giúp tránh collision, dễ debug, dễ invalidate và kiểm soát keyspace.
- Naming convention: dùng
:phân cấp, prefix theo domain/service, lowercase, tránh space và ký tự đặc biệt; thống nhất một quy ước cho cả team. - Namespace (môi trường + service) cô lập key khi dùng chung Redis; ưu tiên prefix trong key hơn là tách theo số DB (đặc biệt với Redis Cluster).
- Versioning key (
user:v2:123) cho phép invalidate hàng loạt khi đổi format: chỉ tăng version trong code, cache cũ tự hết hạn. - Mọi tham số ảnh hưởng kết quả (query, locale, page) phải nằm trong key; khi key dài thì hash phần tham số (đã chuẩn hoá/sort) bằng SHA-1/MD5.
- Mỗi key tốn RAM; cân bằng readable và ngắn gọn, và tránh keyspace vô hạn từ input tự do (dùng TTL, hash, maxmemory + eviction).
Quiz 5 câu
- Vì sao thiết kế cache key lại quan trọng trong một Redis dùng chung? Nêu ít nhất ba hệ quả của thiết kế kém.
- Khác biệt giữa namespace và versioning trong cache key là gì? Mỗi cái giải quyết bài toán nào?
- Một endpoint danh sách phụ thuộc vào
category,locale,page. Điều gì xảy ra nếu bạn bỏpagera khỏi key? Cách build key đúng? - Khi nào nên hash phần tham số của key? Vì sao bắt buộc phải chuẩn hoá (sort) tham số trước khi hash?
- Vì sao không nên dùng
KEYS prefix:*để xoá nhóm key trên production? Có những cách thay thế nào?
Đáp án gợi ý
- Vì keyspace phẳng và dùng chung, tên key là cấu trúc duy nhất để phân tách. Thiết kế kém dẫn tới: (1) collision/đụng key giữa service/môi trường gây ghi đè và đọc nhầm; (2) khó debug vì key mơ hồ; (3) không invalidate được theo nhóm; thêm nữa là keyspace phình to khó kiểm soát và dễ tạo big key.
- Namespace là tiền tố cô lập keyspace theo môi trường/service (
prod:billing:...) để nhiều thành phần dùng chung Redis không đụng nhau. Versioning là số version nhúng trong key (user:v2:...) để invalidate hàng loạt khi đổi format: tăng version thì key cũ không còn được đọc và tự hết hạn. - Nếu bỏ
page, mọi trang dùng chung một key nên trang 2 sẽ đọc nhầm cache của trang 1 (hoặc ngược lại). Build key đúng phải gồm mọi tham số ảnh hưởng kết quả, ví dụprod:product:v1:list:cat=phone:locale=vi:page=2, hoặc hash phần tham số đã chuẩn hoá. - Nên hash khi key có nhiều tham số khiến nó quá dài hoặc khó đọc/khó dùng. Bắt buộc sort (chuẩn hoá) tham số trước khi hash để hai request giống nhau về ngữ nghĩa nhưng khác thứ tự (
a=1&b=2vsb=2&a=1) cho ra cùng một hash, tránh sinh trùng key và tụt hit ratio. KEYSquét toàn bộ keyspace và chặn server đơn luồng, gây tăng latency hoặc treo trên production lớn. Thay bằngSCAN(lặp theo cursor, không chặn), hoặc chủ động gom key liên quan vào một Set / gắn tag để xoá có chủ đích, hoặc dùng versioning để cache cũ tự hết hạn thay vì xoá thủ công.
Bài tiếp theo
Bài 12 đi vào chiến lược TTL: chọn thời gian sống cho từng loại dữ liệu, dùng EX/PX/EXPIRE, thêm jitter để tránh hết hạn đồng loạt, và cân bằng giữa độ tươi dữ liệu với tải database — nối tiếp trực tiếp các nguyên tắc về key và invalidation ở bài này.
