Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn là BA cho một sàn thương mại điện tử kiểu Tiki hay Shopee. Đúng 12 giờ trưa ngày sale 12.12, hàng trăm nghìn người cùng mở app, cùng xem trang sản phẩm "iPhone giảm 30%". Nếu mỗi lượt xem đó đều đi thẳng vào database để hỏi "giá bao nhiêu, còn hàng không", database sẽ gục trong vài phút. Hệ thống sập, đơn không đặt được, công ty mất tiền tỷ và mất cả uy tín.
Caching (bộ nhớ đệm) chính là tấm khiên đứng giữa người dùng và database, giúp hệ thống trả lời nhanh mà không phải làm lại cùng một công việc hàng triệu lần. Đây là một trong những kỹ thuật có tỷ lệ "đầu tư thấp — hiệu quả cao" nhất trong toàn bộ kiến trúc hệ thống.
Nhiều BA nghĩ caching là chuyện của dev, không liên quan đến mình. Đó là một sai lầm tốn kém. Khi bạn viết spec mà bỏ qua caching, bạn vô tình tạo ra những yêu cầu mâu thuẫn: vừa muốn dữ liệu "luôn mới nhất từng giây", vừa muốn hệ thống "chịu được 100 nghìn người dùng đồng thời" mà chi phí hạ tầng thấp. Hai điều này thường loại trừ nhau, và caching chính là nơi bạn đưa ra đánh đổi (trade-off). Một Technical BA hiểu caching sẽ viết được những yêu cầu khả thi, biết hỏi đúng câu hỏi với kiến trúc sư, và giải thích được cho stakeholder vì sao "dữ liệu trễ 5 phút" đôi khi lại là lựa chọn đúng đắn.
Khái niệm cốt lõi
Caching về bản chất rất đơn giản: lưu lại kết quả của một việc tốn công, để lần sau dùng lại mà không phải làm lại. Cái khó không nằm ở việc lưu, mà nằm ở việc biết khi nào dữ liệu trong cache đã "cũ" và cần làm mới.
Caching diễn ra ở nhiều tầng (layer)
Dữ liệu đi từ database đến mắt người dùng phải qua nhiều chặng, và mỗi chặng đều có thể đặt một lớp cache:
1. Browser cache (cache trên trình duyệt người dùng). Trình duyệt tự lưu lại ảnh, file CSS, JavaScript đã tải. Server điều khiển hành vi này qua các HTTP header:
Cache-Control: ví dụmax-age=86400nghĩa là "giữ file này 86400 giây (1 ngày), đừng hỏi lại server".ETag: một "dấu vân tay" của nội dung. Lần sau trình duyệt hỏi "file này còn vân tay X không?", nếu chưa đổi server trả về304 Not Modified(không gửi lại nội dung, tiết kiệm băng thông).
3. Application cache / cache phía server (Redis, Memcached). Đây là nơi Redis tỏa sáng. Redis là một kho lưu trữ dữ liệu trong bộ nhớ RAM (in-memory), cực nhanh — đọc/ghi tính bằng micro giây. Ứng dụng dùng Redis để lưu những kết quả tính toán nặng: phiên đăng nhập (session), giỏ hàng, bảng xếp hạng, kết quả truy vấn phức tạp, đếm số lượt xem... Vì nằm trong RAM nên nó nhanh hơn database (lưu trên đĩa) hàng chục đến hàng trăm lần.
4. Database cache. Bản thân database cũng có cache nội bộ. Tầng này thường do DBA lo, BA ít can thiệp trực tiếp.
Các "mẫu" caching cần biết
- Cache-aside (lazy loading): Ứng dụng hỏi cache trước. Nếu có (cache hit) thì dùng luôn; nếu không có (cache miss) thì đi lấy từ database, rồi ghi vào cache cho lần sau. Đây là mẫu phổ biến nhất.
- Write-through: Mỗi khi ghi dữ liệu, ghi vào cache và database cùng lúc. Cache luôn tươi nhưng ghi chậm hơn.
- TTL (Time To Live): Đặt "hạn sử dụng" cho mỗi mục trong cache. Hết hạn thì tự xóa. Ví dụ giá vàng cache 60 giây, danh mục sản phẩm cache 1 giờ.
Khái niệm sống còn: Cache Invalidation
Có một câu nói nổi tiếng trong giới lập trình: "Hai vấn đề khó nhất khoa học máy tính là đặt tên biến và cache invalidation" (làm mất hiệu lực cache). Invalidation là việc quyết định khi nào dữ liệu trong cache đã lỗi thời và cần xóa/cập nhật. Cache sai nghĩa là người dùng thấy giá cũ, tồn kho cũ, thông tin sai. Đây là phần BA phải làm rõ nhất trong spec.
Tình huống thực tế
Ví dụ 1 — Sàn TMĐT "MuaNhanh" và ngày sale 12.12
MuaNhanh (giả định) là sàn TMĐT Việt Nam với khoảng 2 triệu người dùng. Ngày thường họ chịu được tải, nhưng mỗi đợt flash sale là database CPU chạm 100% và app treo. Đội kỹ thuật phân tích log thì thấy: 80% truy vấn đến database là để đọc trang chi tiết sản phẩm — mà nội dung trang đó gần như không đổi trong suốt đợt sale.
Giải pháp: BA cùng kiến trúc sư đưa ra spec đặt Redis làm cache-aside cho trang sản phẩm với TTL 30 giây. Nghĩa là cứ 30 giây mới hỏi database một lần cho mỗi sản phẩm, các lượt xem còn lại đọc thẳng từ Redis. Kết quả: số truy vấn đọc xuống database giảm khoảng 95%, thời gian phản hồi trang từ 800ms còn 40ms. Riêng phần tồn kho (số lượng còn lại) thì KHÔNG cache theo TTL mà cập nhật ngay khi có đơn — vì hiển thị sai tồn kho dẫn đến "bán quá số lượng" (oversell), khách đặt được rồi bị hủy, rất tệ.
Bài học: Không phải cứ cache là cache hết. BA phải phân loại từng trường dữ liệu theo mức độ chấp nhận được sự "cũ" (staleness). Giá và mô tả: cache thoải mái. Tồn kho và số dư: cẩn trọng tối đa.
Ví dụ 2 — Báo điện tử VnTin và sự cố CDN cache nhầm trang cá nhân
VnTin (giả định) là một trang tin tức lớn, dùng Cloudflare CDN cache toàn bộ trang chủ và bài viết để chịu được lúc có tin nóng. Một ngày, đội kỹ thuật bật cache quá rộng và vô tình cache luôn cả trang "Tài khoản của tôi". Hậu quả: người dùng A đăng nhập, CDN cache lại trang có tên và email của A, rồi phục vụ chính trang đó cho người dùng B. B mở web thấy thông tin của A. Đây là một sự cố rò rỉ dữ liệu nghiêm trọng.
Nguyên nhân: CDN không phân biệt được trang nào là "công khai dùng chung" (public) và trang nào là "riêng từng người" (private, có chứa cookie phiên đăng nhập). Quy tắc đúng là: trang cá nhân phải gắn header Cache-Control: private, no-store để CDN tuyệt đối không cache.
Bài học: Khi viết spec liên quan CDN, BA bắt buộc phải nêu rõ: trang nào public cache được, trang nào private cấm cache. Đây là yêu cầu giao thoa giữa hiệu năng và bảo mật — đúng đất diễn của Technical BA.
Ví dụ 3 — Ngân hàng số và "cache stampede" lúc làm mới tỷ giá
Một ngân hàng số ở Đông Nam Á cache tỷ giá ngoại tệ trong Redis với TTL 60 giây. Vấn đề phát sinh: đúng giây thứ 60 khi cache hết hạn, có 5.000 request cùng lúc nhận thấy "cache trống" và cùng lúc lao vào database để lấy tỷ giá mới. Database bị đấm một phát 5.000 truy vấn giống hệt nhau, gây giật lag. Hiện tượng này gọi là cache stampede (giẫm đạp cache) hay thundering herd.
Giải pháp ghi vào spec: chỉ cho phép một request được "đi lấy" dữ liệu mới (dùng cơ chế khóa — lock), những request còn lại chờ kết quả đó hoặc tạm dùng giá trị cũ. Đồng thời làm mới cache chủ động trước khi hết hạn (refresh-ahead).
Bài học: TTL không chỉ là một con số. BA cần hỏi: "Khi cache hết hạn cùng lúc, điều gì xảy ra?" Câu hỏi này phân biệt một BA bình thường với một Technical BA.
Hướng dẫn từng bước
Khi bạn phụ trách một tính năng và cần đưa caching vào spec, hãy đi theo trình tự sau:
Bước 1 — Xác định dữ liệu nào nên cache. Tìm dữ liệu được đọc nhiều, ghi ít, và đắt khi tạo ra (truy vấn nặng, gọi API bên thứ ba...). Ví dụ: danh mục sản phẩm, cấu hình hệ thống, kết quả báo cáo. Dữ liệu thay đổi liên tục từng giây và yêu cầu chính xác tuyệt đối (số dư tài khoản) thì cân nhắc rất kỹ.
Bước 2 — Định nghĩa mức độ "tươi" chấp nhận được. Với mỗi loại dữ liệu, hỏi business: "Trễ bao lâu thì vẫn chấp nhận được?" Câu trả lời thành TTL. Ví dụ: "Top sản phẩm bán chạy trễ 10 phút không sao" → TTL 600 giây.
Bước 3 — Chọn tầng cache phù hợp. Ảnh, CSS, video → CDN + browser cache. Kết quả truy vấn, session, giỏ hàng → Redis. Quy tắc: cache càng gần người dùng càng nhanh, nhưng càng khó kiểm soát việc làm mới.
Bước 4 — Định nghĩa quy tắc invalidation. Đây là phần quan trọng nhất. Viết rõ: khi sự kiện gì xảy ra thì cache bị xóa/cập nhật? Ví dụ: "Khi admin sửa giá sản phẩm → xóa ngay cache của sản phẩm đó, không chờ TTL."
Bước 5 — Xử lý các tình huống biên. Ghi vào spec: cache miss thì sao? Redis chết thì sao (hệ thống vẫn chạy được không, hay sập theo)? Cache stampede xử lý thế nào? Dữ liệu private có lỡ bị cache không?
Bước 6 — Định nghĩa chỉ số đo lường. Yêu cầu hệ thống báo cáo cache hit ratio (tỷ lệ trúng cache). Hit ratio 90% nghĩa là 90% request được phục vụ từ cache. Nếu chỉ số này thấp bất thường, cache đang không hiệu quả.
Lỗi thường gặp & mẹo
Lỗi 1 — Cache mọi thứ. Cache dữ liệu cá nhân, dữ liệu nhạy cảm về tiền bạc một cách bừa bãi. Hậu quả như ví dụ VnTin. Mẹo: mặc định coi dữ liệu là "không cache", chỉ cache khi đã chứng minh được nó an toàn và đáng cache.
Lỗi 2 — Quên quy tắc invalidation. Spec chỉ ghi "cache trang sản phẩm" mà không ghi "khi nào xóa cache". Kết quả: admin sửa giá xong, khách vẫn thấy giá cũ cả tiếng đồng hồ. Mẹo: với mỗi cache, luôn viết kèm câu "Cache này được làm mới/xóa khi: ...".
Lỗi 3 — Coi Redis là database. Cache có thể bị xóa bất cứ lúc nào (hết RAM, restart). Nếu bạn lưu dữ liệu DUY NHẤT trong cache mà không có ở database, mất cache là mất dữ liệu. Mẹo: cache chỉ là bản sao tạm, nguồn sự thật (source of truth) luôn là database.
Lỗi 4 — Bỏ qua kịch bản "cache chết". Mẹo: luôn hỏi "Nếu Redis sập, hệ thống có còn chạy không?" Câu trả lời lý tưởng là "chậm hơn nhưng vẫn chạy" (graceful degradation), không phải "sập toàn bộ".
Lỗi 5 — TTL quá dài hoặc quá ngắn. Quá dài → dữ liệu cũ; quá ngắn → cache vô dụng, database vẫn nặng. Mẹo: bắt đầu từ nhu cầu nghiệp vụ ("trễ bao lâu chấp nhận được"), không tự bịa số.
Mẹo vàng cho BA: Trong mọi cuộc họp về caching, hãy luôn đặt ra ba câu hỏi: (1) Dữ liệu này được phép cũ bao lâu? (2) Khi nào nó phải được làm mới ngay? (3) Nếu cache sai/chết thì hậu quả nghiệp vụ là gì? Ba câu này gói gọn 90% giá trị bạn mang lại.
Bài tập thực hành
Tình huống: Bạn là Technical BA cho ứng dụng đặt vé xem phim "XemPhim" (giả định). App có các màn hình: trang chủ (banner phim đang chiếu), trang chi tiết phim, sơ đồ ghế ngồi của một suất chiếu, và trang "Vé của tôi".
Yêu cầu:
- Lập bảng phân loại 4 loại dữ liệu trên: loại nào nên cache, đặt ở tầng nào (CDN/Redis/không cache), và TTL đề xuất là bao nhiêu. Giải thích lý do cho từng loại.
- Riêng với sơ đồ ghế ngồi — nơi hiển thị ghế nào còn trống, ghế nào đã bán — hãy lập luận: có nên cache không? Nếu có thì xử lý invalidation thế nào để hai khách không cùng đặt một ghế? Đây là phần khó nhất, hãy suy nghĩ về tình huống cache stampede và oversell.
- Trang "Vé của tôi" chứa thông tin cá nhân. Viết một dòng yêu cầu (requirement) về caching cho trang này, dùng đúng thuật ngữ HTTP header mà bạn đã học.
- Viết một câu hỏi bạn sẽ đặt cho kiến trúc sư về kịch bản "Redis sập giữa giờ cao điểm cuối tuần".
Cache-Control: private, no-store.Tóm tắt
Caching là kỹ thuật lưu lại kết quả tốn công để tái sử dụng, giúp hệ thống nhanh hơn và chịu tải tốt hơn với chi phí thấp. Dữ liệu được cache ở nhiều tầng: browser cache (qua header Cache-Control, ETag), CDN (Cloudflare, CloudFront — đưa nội dung đến gần người dùng), và application cache như Redis (kho dữ liệu trong RAM, cực nhanh, dùng cho session, giỏ hàng, kết quả truy vấn).
Với vai trò Technical BA, bạn không cần code Redis, nhưng bạn phải làm chủ các quyết định nghiệp vụ quanh nó: dữ liệu nào được cache, được phép "cũ" bao lâu (TTL), khi nào phải làm mới ngay (invalidation), và điều gì xảy ra khi cache sai hoặc cache chết. Ba sự cố kinh điển cần nhớ: rò rỉ dữ liệu private qua CDN, oversell do cache tồn kho sai, và cache stampede khi nhiều request cùng làm mới. Hãy luôn ghi rõ quy tắc invalidation trong spec — đó là phần phân biệt một BA giỏi với một BA thực sự hiểu hệ thống.