Họ đang đo gì
Bạn có nghĩ tới lúc cache TRƯỢT không, và tới việc nhiều tiến trình cùng trượt một lúc.
Trả lời ngắn~30 giây
Cache-aside là mặc định: đọc thì thử cache trước, trượt thì đọc DB rồi ghi vào cache; ghi thì cập nhật DB rồi XOÁ khoá, không phải cập nhật khoá. Xoá an toàn hơn cập nhật vì hai lần ghi đồng thời có thể ghi ngược thứ tự vào cache. Cái bẫy lớn nhất không phải dữ liệu cũ mà là stampede: một khoá nóng hết hạn, năm nghìn request cùng trượt và cùng lao vào database.
Cache-aside: đọc thử cache trước, trúng thì trả luôn. Trong khi TTL còn hiệu lực, database gần như không nhận gì.
Giải thích sâu
Chống stampede có ba cách, dùng chung được. Một là khoá: request đầu tiên trượt sẽ giành một khoá ngắn trong Redis và đi tính, các request khác chờ hoặc trả tạm giá trị cũ. Hai là hết hạn sớm ngẫu nhiên: mỗi request tự quyết định làm mới sớm với xác suất tăng dần khi gần hết hạn, nên việc làm mới trải đều thay vì dồn vào một mốc. Ba là TTL có nhiễu — thêm ±10% ngẫu nhiên vào TTL để hàng nghìn khoá được tạo cùng lúc không hết hạn cùng lúc.
Về tính đúng đắn, cần thừa nhận thẳng: cache-aside có một cửa sổ đua không đóng được hoàn toàn. Một request đọc trượt, đọc DB, rồi TRƯỚC KHI nó kịp ghi vào cache có một request khác cập nhật DB và xoá khoá — cuối cùng request đầu ghi giá trị cũ vào cache và nó nằm đó tới hết TTL. Xác suất thấp nhưng khác không. Cách giảm là TTL ngắn, và với dữ liệu thật sự không được cũ thì đừng cache nó.
Điều đáng nói ở mức senior là chọn khoá và mức chi tiết. Cache một object nhỏ theo id thì tỷ lệ trúng cao và vô hiệu hoá dễ; cache cả một trang kết quả đã lọc và sắp xếp thì tỷ lệ trúng thấp và bất kỳ thay đổi nào cũng làm nó sai. Nếu bạn phải xoá theo mẫu (KEYS user:*) thì thiết kế khoá đã sai — KEYS chặn Redis, và nhu cầu đó thường có nghĩa là bạn nên cache ở mức chi tiết hơn.
Câu hỏi tiếp theo họ sẽ hỏi
?Write-through và write-behind khác gì?
Write-through ghi cache và DB cùng lúc — cache luôn mới, nhưng mọi lần ghi chậm hơn và bạn cache cả những thứ không ai đọc. Write-behind ghi cache trước rồi đẩy xuống DB sau — nhanh nhất và nguy hiểm nhất, vì mất cache là mất dữ liệu chưa kịp xuống.
?Cache một giá trị không tồn tại?
Có, và nên. Không cache negative thì mọi truy vấn id không tồn tại đều xuống database — đây chính là cache penetration, và nó bị lợi dụng để tấn công rất dễ. Cache null với TTL ngắn hoặc dùng bộ lọc Bloom cho tập id hợp lệ.
Trả lời thế này là mất điểm
- Cập nhật cache thay vì xoá khoá, mà không nói tới thứ tự ghi. Đây là nguồn dữ liệu sai dai dẳng.
- Không nhắc tới stampede. Nó là sự cố thật đầu tiên mọi cache đều gặp.
Nguồn
- Redis — Client-side caching và mẫu vô hiệu hoá
- Vattani, Chierichetti, Lowenstein — Optimal Probabilistic Cache Stampede Prevention (VLDB 2015)