Caching
Đặt cache ở đâu (client, CDN, app, Redis), các chiến lược đọc ghi cache-aside, write-through, write-back, write-around, eviction LRU/LFU/TTL, invalidation, chống stampede và hot key, cách tính RAM Redis và toán hit ratio.
Cache là gì
Cache là một bản sao dữ liệu nằm ở chỗ đọc nhanh hơn nguồn gốc. Nguồn gốc thường là database, một service khác, hoặc một phép tính tốn CPU. Bản sao nằm trong RAM, gần người dùng hơn, hoặc đã được tính sẵn. Grokking định nghĩa cache là vùng lưu trữ tạm, không bền, giữ dữ liệu được đọc đi đọc lại trong bộ nhớ để phục vụ nhanh hơn; trượt cache thì đọc từ database rồi ghi lại vào cache.1
Bảng dưới cho thấy vì sao cache đáng dùng (ước lượng, các mốc theo Grokking ch.5 và Redis in Action ch.2):23
| Thao tác | Độ trễ điển hình |
|---|---|
| Đọc biến trong RAM của process | dưới 1 µs |
| Đọc Redis cùng data center | ~0,5–1 ms |
| Query Postgres có index, dữ liệu nằm trong buffer | ~1–5 ms |
| Query Postgres phải đọc đĩa hoặc join nặng | 10–100 ms trở lên |
| Gọi API từ Việt Nam sang Mỹ | ~150–250 ms |
Grokking liệt kê RAM, round trip trong cùng data center cỡ trăm micro giây, seek đĩa cỡ vài ms, gói tin xuyên lục địa cỡ vài chục ms.2 Redis in Action nói một trang động tốn 20–50 ms có thể giảm còn một round trip tới Redis (dưới 1 ms nếu cùng máy, dưới 5 ms nếu cùng data center).3 Các dòng cụ thể về Postgres và API Việt Nam sang Mỹ là ước lượng của khoá học, cần đo lại.
Cache có giá. Nó là một bản sao nên có thể cũ. Nó tốn RAM, và RAM đắt hơn đĩa nhiều lần. Nó thêm một thành phần có thể hỏng. Mọi quyết định cache đều xoay quanh câu hỏi: dữ liệu này được phép cũ bao lâu, và nếu cache mất hết thì nguồn gốc có chịu nổi không. Grokking giải thích RAM được chọn làm nền cho cache vì đủ nhanh, đủ dung lượng và chi phí chấp nhận được; và vì cache là vùng không bền nên có thể mất dữ liệu.1
Vì sao quan trọng trong phỏng vấn
Hầu hết bài thiết kế có tỷ lệ đọc ghi lệch hẳn về đọc: 10:1, 100:1, có khi 1000:1. Khi bạn tính ra 50.000 read QPS mà một database chỉ chịu cỡ vài nghìn query đơn giản mỗi giây (Grokking lấy mốc 1.000 QPS cho MySQL, 100.000 tới 1 triệu cho cache server), cache là câu trả lời đầu tiên người phỏng vấn chờ.41
Nhưng nói "thêm Redis" thôi thì chưa được điểm. Người phỏng vấn sẽ hỏi tiếp:
- Cache cái gì, key là gì, TTL bao lâu?
- Ghi xong thì cache cập nhật thế nào?
- Một key cực nóng hết hạn thì chuyện gì xảy ra?
- Redis chết thì database có sập theo không?
- Cần bao nhiêu RAM, bao nhiêu node?
Ứng viên tốt trả lời được cả năm câu bằng con số và cơ chế cụ thể.
Cache đặt ở đâu
Một request đi qua nhiều tầng, tầng nào cũng có thể cache. Càng gần người dùng thì càng nhanh, nhưng càng khó xóa khi dữ liệu đổi (nhận xét của khoá học).
| Tầng | Ví dụ | Hợp với | Khó ở đâu |
|---|---|---|---|
| Client | HTTP Cache-Control, React Query, localStorage | Ảnh, JS, CSS, dữ liệu cá nhân ít đổi | Không xóa được từ server, chỉ chờ hết hạn hoặc đổi URL |
| CDN | CloudFront, Cloudflare | File tĩnh, video, ảnh, trang công khai | Purge mất vài giây tới vài phút, không hợp dữ liệu riêng từng user |
| App local | Map trong process, lru-cache | Config, danh mục nhỏ, key cực nóng | Mỗi instance có bản riêng, lệch nhau, mất khi restart |
| Distributed | Redis, Memcached | Session, profile, kết quả query, counter | Thêm một network hop, cần sizing và failover |
| Trong database | Buffer pool Postgres, materialized view | Có sẵn, không phải làm gì | Bị giới hạn bởi RAM của máy DB |
Grokking ch.16 liệt kê cache theo ba tầng hệ thống: tầng web (HTTP cache header, web accelerator, CDN, key-value store), tầng ứng dụng (cache local và key-value store) và tầng database (database cache, buffer); ngoài ra còn cache ở DNS và phía client như trình duyệt.5 Mỗi hàng của bảng trên là cách khoá học áp các tầng đó vào ví dụ cụ thể, các công cụ tên riêng lấy từ sách hoặc là ví dụ minh họa. Trong phỏng vấn, thường bạn nói tới hai hoặc ba tầng: CDN cho nội dung tĩnh, Redis cho dữ liệu động, đôi khi thêm cache local cho key cực nóng.
Các chiến lược đọc và ghi
Cache-aside (lazy loading)
Ứng dụng tự quản lý cache. Đọc thì hỏi cache trước, miss thì đọc DB rồi ghi vào cache. Ghi thì ghi DB rồi xóa key trong cache. Đọc theo kiểu này khớp với mô tả "trượt thì đọc database rồi nạp lại cache" của Grokking, và với hàm cache_request() của Redis in Action (lấy từ cache, không có thì sinh nội dung, setex 300 giây rồi trả về).13
async function getUser(id: string) {
const key = `user:${id}`;
const hit = await redis.get(key);
if (hit) return JSON.parse(hit);
const user = await db.users.findById(id);
if (user) await redis.set(key, JSON.stringify(user), "EX", 300);
return user;
}
async function updateUser(id: string, patch: Partial<User>) {
await db.users.update(id, patch);
await redis.del(`user:${id}`); // xóa, không SET giá trị mới
}
Xóa key thay vì ghi giá trị mới vì hai request ghi đồng thời có thể ghi cache theo thứ tự ngược với DB, để lại giá trị sai cho tới khi hết TTL. Xóa thì lần đọc sau sẽ lấy bản đúng từ DB.6
Write-through
Mỗi lần ghi đi qua cache và cache ghi xuống DB đồng bộ, hoặc app ghi cả hai trong cùng một bước. Cache luôn có dữ liệu mới. Đổi lại, mỗi lần ghi chậm hơn.7 Cache có thể chứa cả những thứ không ai đọc (nhận xét của khoá học).
Write-back (write-behind)
Ghi vào cache rồi trả về ngay, cache gom lại và ghi xuống DB sau. Ghi cực nhanh, gộp được nhiều lần cập nhật thành một. Sách viết: ghi vào cache trước, ghi bất đồng bộ xuống database, độ trễ ghi nhỏ nhưng không tránh được bất nhất khi client đọc dữ liệu cũ từ database.7 Rủi ro mất dữ liệu khi cache chết trước khi flush là hệ quả của việc cache không bền. Hợp với counter view, like, vị trí GPS, những thứ mất vài giây dữ liệu không sao (ví dụ của khoá học).
Write-around
Ghi thẳng vào DB, không đụng cache. Cache chỉ được nạp khi có người đọc và trượt cache. Sách nói chiến lược này không thuận lợi khi cần đọc lại ngay dữ liệu vừa cập nhật.7 Hợp với dữ liệu ghi nhiều nhưng ít khi đọc lại ngay, như log, bản ghi lịch sử (ví dụ của khoá học).
| Chiến lược | Ghi | Đọc ngay sau ghi | Rủi ro | Dùng khi |
|---|---|---|---|---|
| Cache-aside | DB rồi xóa cache | Miss một lần | Dữ liệu cũ nếu quên xóa | Mặc định cho hầu hết bài |
| Write-through | Cache và DB đồng bộ | Hit | Ghi chậm, cache phình | Dữ liệu đọc ngay sau ghi, như profile, giỏ hàng |
| Write-back | Cache, DB sau | Hit | Mất dữ liệu khi cache chết | Counter, metric, dữ liệu chấp nhận mất ít |
| Write-around | Chỉ DB | Miss | Lần đọc đầu chậm | Ghi nhiều, đọc ít |
Grokking ch.16 mô tả ba chính sách ghi write-through, write-back, write-around và trả lời rằng chọn chính sách nào "tùy yêu cầu của ứng dụng", với hệ quả khác nhau về mô hình nhất quán: write-through tăng độ trễ ghi nhưng giữ nhất quán mạnh, write-back độ trễ ghi nhỏ nhưng không nhất quán.7 Cache-aside không phải một trong ba chính sách đó của Grokking; Redis in Action dùng nó ở ví dụ cache trang và cache dòng database.3 Cột "Dùng khi" và "Rủi ro" của bảng là tóm tắt của khoá học.
Eviction và invalidation
Eviction: hết RAM thì bỏ cái gì
| Chính sách | Bỏ key nào | Hợp với | Điểm yếu |
|---|---|---|---|
| LRU | Lâu nhất chưa được đọc | Đa số workload web | Một lần quét lớn đẩy hết dữ liệu nóng ra |
| LFU | Ít được đọc nhất | Dữ liệu nóng ổn định lâu, như sản phẩm bán chạy | Key cũ từng nóng bám lại lâu |
| TTL | Hết hạn theo thời gian | Mọi thứ có thể cũ | Không giải quyết hết RAM nếu TTL dài |
Grokking liệt kê LRU, MRU, LFU, MFU, ngoài ra có FIFO, và nói lựa chọn tùy hệ thống đang xây; mục đích là bỏ dữ liệu ít được truy cập khi cache nhỏ, giới hạn dung lượng.8 TTL trong sách là cơ chế xử lý dữ liệu lỗi thời (active expiration: tiến trình nền kiểm tra TTL; passive expiration: kiểm tra khi truy cập).9 Cột "Hợp với" và "Điểm yếu" là nhận xét của khoá học. Node.js Design Patterns cũng khuyên dùng LRU để giữ mức bộ nhớ không đổi khi cache lớn dần.10
Redis dùng LRU và LFU xấp xỉ, cấu hình qua maxmemory-policy.11 Grokking ch.16 cho một ví dụ: với cùng độ trễ hit 5 ms và miss 30 ms, thuật toán có miss 5% (LRU) cho thời gian truy cập hiệu dụng 6,25 ms, thuật toán có miss 10% (MFU) cho 7,5 ms; sách kết luận hit ratio phụ thuộc eviction và mỗi ứng dụng nên đo thực nghiệm.12
Invalidation: dữ liệu đổi thì cache biết bằng cách nào
- TTL: đơn giản, chấp nhận cũ tối đa bằng TTL. Sách dùng TTL gắn với mỗi entry để phát hiện entry lỗi thời.9 Luôn đặt TTL, kể cả khi đã có cách xóa khác, để lỗi nào cũng tự lành (khuyến nghị của khoá học).
- Xóa khi ghi: cache-aside xóa key ngay sau khi ghi DB. Nhanh, nhưng phải nhớ xóa ở mọi chỗ ghi. Node.js Design Patterns cũng nói invalidation thủ công cho phép cache sống lâu hơn và dữ liệu mới hơn nhưng phức tạp hơn nhiều để quản lý.10
- Xóa theo sự kiện: đọc thay đổi từ DB qua Change Data Capture rồi xóa key. Không phụ thuộc code ứng dụng nhớ xóa (xem bài CDC của khoá học).
- Version trong key:
product:42:v17. Đổi dữ liệu thì tăng version, key cũ tự hết hạn. Hợp với CDN, nơi xóa chậm.13
Lưu ý: Xóa cache sau khi ghi DB vẫn có cửa sổ race nhỏ: request đọc lấy dữ liệu cũ từ DB, request ghi cập nhật DB và xóa cache, rồi request đọc mới SET dữ liệu cũ vào. TTL ngắn giới hạn thiệt hại. Một số hệ thống xóa hai lần (xóa ngay và xóa lại sau vài trăm ms) để thu hẹp cửa sổ này.14
Stampede và hot key
Cache stampede
Một key được đọc 5.000 lần mỗi giây hết hạn. Trong 50 ms để DB trả kết quả, 250 request đều miss và cùng query DB (phép tính ví dụ của khoá học). Node.js Design Patterns mô tả đúng hiện tượng này: nhiều request chạy đồng thời khi cache chưa được đặt, và khi chúng xong thì cache bị đặt nhiều lần, nên cần kết hợp caching với batching.15 Với key đắt, DB có thể ngã luôn.
Gộp request trong một process. Node.js Design Patterns ch.9 mô tả cách gộp (request batching): nếu đang gọi một hàm bất đồng bộ trong khi còn một lần gọi giống hệt chưa xong, gắn callback vào thao tác đang chạy thay vì tạo request mới; ở phần "Batching and caching with promises" sách dùng chính promise đang chạy cho việc này. Mỗi instance chỉ gửi một query cho mỗi key.16
const inflight = new Map<string, Promise<unknown>>();
function loadOnce<T>(key: string, loader: () => Promise<T>): Promise<T> {
const running = inflight.get(key);
if (running) return running as Promise<T>;
const p = loader().finally(() => inflight.delete(key));
inflight.set(key, p);
return p;
}
Với 20 instance, DB nhận tối đa 20 query thay vì 250 (phép tính minh họa). Sách nói batching phát huy tốt nhất với ứng dụng tải cao và API chậm.16
Khóa phân tán. Muốn chỉ một request trên toàn cụm nạp lại, dùng khóa trong Redis như Redis in Action §6.2: khóa được lấy bằng SETNX với giá trị là một định danh ngẫu nhiên 128 bit (UUID), và thêm timeout bằng EXPIRE để khóa tự hết hạn nếu người giữ bị crash.17 Áp vào cache: ai lấy được khóa thì query DB và ghi cache, người khác chờ ngắn rồi đọc lại cache hoặc trả giá trị cũ (cách áp dụng của khoá học). Redis hiện đại gộp hai bước thành một lệnh SET key token NX PX ms; sách bản này chưa nói tới.18
Làm mới sớm. Trả giá trị cũ trong khi một tác vụ nền nạp lại khi key còn dưới 10% TTL. Hoặc thêm jitter vào TTL (300 s cộng ngẫu nhiên 0–60 s) để hàng nghìn key nạp cùng lúc không hết hạn cùng một giây.19
Hot key
Một key nằm trên một node Redis. Redis in Action nói Redis làm được 100.000 thao tác mỗi giây trên phần cứng gần đây (tới 225.000 trên phần cứng mạnh).20 Bài đăng của người nổi tiếng hay sản phẩm flash sale có thể vượt mức này. Grokking cũng nêu vấn đề hot key và hướng xử lý: thêm replica giảm tải shard nóng, shard tiếp trong dải key nóng, hoặc dynamic replication cho key cụ thể; sách nói các giải pháp này phức tạp.21
- Cache local trước Redis: giữ key nóng trong RAM app 1–5 giây. 50 instance, mỗi instance hỏi Redis một lần mỗi giây, Redis chỉ thấy 50 ops/s cho key đó (giải pháp và phép tính của khoá học; Grokking chỉ nói client có thể tự tránh bottleneck một key).21
- Nhân bản key: lưu
post:9:copy0tớipost:9:copy9, đọc ngẫu nhiên một bản. Mười bản rải trên nhiều node. Ghi thì phải cập nhật cả mười. Đây là một dạng dynamic replication cho key cụ thể mà Grokking nhắc tới.21 - Đọc từ replica: mỗi shard có thêm replica nhận đọc, chấp nhận trễ replication; Grokking: replication bất đồng bộ trong cache làm tăng hiệu năng nhưng gây bất nhất.2122
Trong phỏng vấn: Khi đề có người nổi tiếng, flash sale hay sự kiện trực tiếp, chủ động nói về hot key trước khi bị hỏi. Một câu "key này tôi cache local 2 giây trên mỗi app server" là đủ cho thấy bạn đã nghĩ tới.
Con số và sizing
Toán hit ratio
Tải xuống DB = read QPS × (1 − hit ratio). Độ trễ trung bình = hit × t_hit + miss × t_miss, chính là công thức effective access time (EAT) của Grokking.12
| Read QPS | Hit ratio | QPS xuống DB | Độ trễ TB (hit 1 ms, miss 20 ms) |
|---|---|---|---|
| 50.000 | 80% | 10.000 | 4,8 ms |
| 50.000 | 95% | 2.500 | 1,95 ms |
| 50.000 | 99% | 500 | 1,19 ms |
Từ 95% lên 99% nghe như chỉ thêm 4 điểm, nhưng tải DB giảm 5 lần. Đây là lý do một bài thiết kế đọc nhiều hay đặt mục tiêu hit ratio 95–99% (mục tiêu tham khảo của khoá học). Bảng dùng hit 1 ms, miss 20 ms là giả định của khoá học. Ngược lại, khi cache chết hoàn toàn, DB phải chịu toàn bộ 50.000 QPS. Nếu DB không chịu được, phải có rate limit hoặc chế độ suy giảm, và cache phải có replica.
Tính RAM cho Redis
Công thức khóa học dùng: RAM = số item × (key + value + ~100 B overhead) × 1,3 (quy tắc ước lượng của khoá học).23
Ví dụ: cache profile cho 10 triệu user hoạt động, key ~30 B, value JSON ~500 B.
- 10.000.000 × (30 + 500 + 100) B = 6,3 GB
- × 1,3 (phân mảnh, buffer) ≈ 8,2 GB (ước lượng)
Thường không cần cache hết. Grokking nói cache chỉ giữ dữ liệu được truy cập thường xuyên nhất, dựa trên locality of reference.1 Quy tắc 80/20 (20% dữ liệu nhận phần lớn lượt đọc) là giả định của khoá học. Cache 20% của 50 triệu user cũng ra cỡ 10 triệu item ở trên.
Số node
| Yêu cầu | Tính | Kết quả (ước lượng của khoá học)23 |
|---|---|---|
| RAM 8 GB | Một node 16 GB đủ, giữ dưới ~70% | 1 primary |
| 250.000 ops/s | 250.000 / 100.000 ops/s mỗi node, chừa dư | 3–4 shard |
| Chịu mất 1 node | Mỗi shard thêm 1 replica | 6–8 node tổng |
Grokking ch.5 nhắc mức tham chiếu một cache server chịu từ 100k tới 1M request mỗi giây, là số xấp xỉ phụ thuộc loại query, máy và thiết kế.4 Dùng mức 100k để tính an toàn. Grokking ch.16 nói pipelining giảm đáng kể độ trễ do RTT, nên mức cao hợp với request nhỏ có pipelining.24
Mẹo: Đọc to phép tính: "10 triệu item, mỗi item khoảng 600 byte, ra 6 GB, nhân 1,3 thành 8 GB. Throughput 250k ops/s nên tôi cần 3 shard, mỗi shard một replica." Ba câu này đủ cho phần sizing.
Sai lầm hay gặp
Sai lầm hay gặp: Cache mọi thứ ngay từ đầu. Cache thêm độ phức tạp và nguồn dữ liệu cũ; Node.js Design Patterns cũng lưu ý cache đầy đủ cần quản lý bộ nhớ và invalidation, và batching đơn giản có thể đủ.1016 Hãy chỉ ra phần nào đọc nhiều, chấp nhận cũ được, và query đắt, rồi chỉ cache phần đó.
- Không đặt TTL. Một lỗi quên xóa sẽ để dữ liệu sai nằm mãi.9
- SET giá trị mới khi ghi thay vì DEL. Hai lần ghi đồng thời làm cache lệch DB.6
- Cache dữ liệu cần nhất quán mạnh rồi tin nó. Số ghế còn trống trong cache chỉ để hiển thị. Bước giữ chỗ phải kiểm tra ở DB. Xem CAP.
- Quên trường hợp cache trống. Sau deploy hoặc Redis restart, mọi request miss cùng lúc. Cần warm-up hoặc gộp request.15
- Cache kết quả "không tìm thấy" không có TTL ngắn, hoặc không cache nó. Không cache thì kẻ xấu gọi ID không tồn tại liên tục để đánh thẳng vào DB. Nên cache giá trị rỗng với TTL ngắn, khoảng 30–60 giây.25
- Value quá lớn. Một key 5 MB làm nghẽn mạng và chặn Redis đơn luồng. Chia nhỏ hoặc nén. Grokking ghi Redis chạy một process dùng một core.26
- Dùng
KEYS *trên production. Lệnh quét toàn bộ, chặn Redis. DùngSCANhoặc đặt TTL.27
Khi nào nhắc tới trong phỏng vấn
Bảng dưới là tóm tắt cách khoá học áp cache vào từng bài; nguồn chi tiết nằm trong từng bài thiết kế. Phần lý thuyết chung của các bài này dựa trên Grokking ch.16.15
| Bài | Điểm cache cần nói |
|---|---|
| Rút gọn URL | Cache-aside cho mapping, hit ratio cao vì link mới được bấm nhiều |
| News feed | Feed tính sẵn trong Redis, hot key cho người nổi tiếng |
| Đặt vé sự kiện | Cache trang sự kiện, không tin cache khi giữ ghế |
| Flash sale | Stampede khi mở bán, cache local cho trang sản phẩm |
| YouTube | CDN cho video, metadata trong Redis |
| CDN cho ảnh, cache profile và feed | |
| Yelp | Cache kết quả tìm kiếm theo vùng |
| Distributed cache | Thiết kế chính Redis: sharding, eviction, replication |
| Rate limiter | Counter trong Redis, write-back |
| Top K | Cache kết quả bảng xếp hạng, làm mới theo chu kỳ |
Đọc tiếp: Redis cho cấu trúc dữ liệu và vận hành, Scale đọc cho bức tranh đầy đủ gồm replica và CDN, Consistent hashing cho cách chia key giữa các node cache.
Nguồn trích dẫn
-
Grokking Modern System Design · ch.16 Distributed Cache · bài "System Design: The Distributed Cache", mục "Problem statement" và "What is a distributed cache?". ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · mục "Standard numbers to remember", bảng "Important Latencies". ↩ ↩2
-
Redis in Action · ch.2 · §2.3 Web page caching (tr. 30–31). ↩ ↩2 ↩3 ↩4
-
Grokking Modern System Design · ch.5 Back-of-the-envelope Calculations · mục "Standard numbers to remember", bảng "Important Rates". ↩ ↩2
-
Grokking Modern System Design · ch.16 Distributed Cache · mục "Why distributed cache?", bảng "Caching at Different Layers of a System". ↩ ↩2
-
Chưa có nguồn trong thư viện: lý do nên xóa key thay vì ghi giá trị mới khi cập nhật là kinh nghiệm thực hành, sách chỉ mô tả cache-aside ở mức đọc. ↩ ↩2
-
Grokking Modern System Design · ch.16 Distributed Cache · bài "Background of Distributed Cache", mục "Writing policies". ↩ ↩2 ↩3 ↩4
-
Grokking Modern System Design · ch.16 Distributed Cache · bài "Background of Distributed Cache", mục "Eviction policies". ↩
-
Grokking Modern System Design · ch.16 Distributed Cache · bài "Background of Distributed Cache", mục "Cache invalidation". ↩ ↩2 ↩3
-
Node.js Design Patterns 2e (file .zip, tải về giải nén) · ch.9 Advanced Asynchronous Recipes · mục "Notes about implementing caching mechanisms" (tr. 368–369). ↩ ↩2 ↩3
-
Chưa có nguồn trong thư viện: cấu hình
maxmemory-policyvà việc Redis xấp xỉ LRU/LFU không có trong các sách của thư viện. ↩ -
Grokking Modern System Design · ch.16 Distributed Cache · bài "Evaluation of a Distributed Cache Design", mục "High performance" (công thức EAT). ↩ ↩2
-
Chưa có nguồn trong thư viện: kỹ thuật đặt version trong key là kinh nghiệm thực hành. ↩
-
Chưa có nguồn trong thư viện: cửa sổ race của cache-aside và kỹ thuật xóa hai lần là kinh nghiệm thực hành. ↩
-
Node.js Design Patterns 2e (file .zip, tải về giải nén) · ch.9 Advanced Asynchronous Recipes · mục "Asynchronous request caching" (tr. 365). ↩ ↩2
-
Node.js Design Patterns 2e (file .zip, tải về giải nén) · ch.9 Advanced Asynchronous Recipes · mục "Asynchronous request batching" và "Batching and caching with promises" (tr. 362–369). ↩ ↩2 ↩3
-
Redis in Action · ch.6 · §6.2.3 Building a lock in Redis và §6.2.5 Locks with timeouts (tr. 121–126). ↩
-
Chưa có nguồn trong thư viện: lệnh
SET ... NX PXra đời sau bản Redis in Action trong thư viện; chỉ nêu để đối chiếu vớiSETNXvàEXPIREtrong sách. ↩ -
Chưa có nguồn trong thư viện: làm mới sớm và jitter cho TTL là kỹ thuật phổ biến, chưa tìm thấy trong sách. ↩
-
Redis in Action · ch.6 · §6.2.2 Simple locks (tr. 120–121). ↩
-
Grokking Modern System Design · ch.16 Distributed Cache · bài "Evaluation of a Distributed Cache Design", mục "Scalability" (hotkeys). ↩ ↩2 ↩3 ↩4
-
Grokking Modern System Design · ch.16 Distributed Cache · bài "Evaluation of a Distributed Cache Design", mục "Consistency". ↩
-
Quy tắc ước lượng của khoá học, xem bài Chọn server. ↩ ↩2
-
Grokking Modern System Design · ch.16 Distributed Cache · bài "Memcached versus Redis", mục "Pipelining in Redis". ↩
-
Chưa có nguồn trong thư viện: cache kết quả "không tìm thấy" với TTL ngắn là thực hành phổ biến, TTL 30–60 giây là gợi ý của khoá học. ↩
-
Grokking Modern System Design · ch.16 Distributed Cache · bài "Memcached versus Redis", so sánh "Multithreading". ↩
-
Chưa có nguồn trong thư viện: cảnh báo về
KEYS *vàSCANkhông có trong các sách của thư viện. ↩