Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Caching strategies: TTL, write-through, write-behind

API and Technical Fundamentals cho BA Bài 35/60

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ử Việt Nam. Mỗi sáng thứ Hai, trang chủ hiển thị danh sách "Sản phẩm bán chạy". Danh sách này thay đổi rất chậm — có khi cả ngày mới cập nhật một lần. Vậy mà mỗi lần khách mở app, hệ thống lại chạy một câu truy vấn nặng vào database để tính toán lại từ đầu. Với 500.000 lượt mở app/ngày, database của bạn gồng gánh 500.000 lần tính toán cho một kết quả gần như không đổi. Đây chính là vấn đề mà caching (bộ nhớ đệm) ra đời để giải quyết.

Là một BA, bạn không phải người viết code caching. Nhưng bạn là người ngồi trong phòng họp khi team kỹ thuật tranh luận: "Cache cái này bao lâu thì hợp lý?", "Nếu giá sản phẩm đổi mà cache chưa hết hạn thì sao?", "Khách sẽ thấy dữ liệu cũ trong bao lâu là chấp nhận được?". Những câu hỏi này không phải câu hỏi kỹ thuật thuần túy — chúng là câu hỏi nghiệp vụ. Và bạn chính là người phải đưa ra câu trả lời. Bài học này giúp bạn nắm vững ba chiến lược cache cốt lõi — TTL, write-through, write-behind — đủ để bạn tham gia thiết kế, đánh giá rủi ro và viết yêu cầu một cách tự tin.

Khái niệm cốt lõi

Cache là gì và vì sao nó nhanh đến vậy

Cache là một lớp lưu trữ tạm thời, đặt gần nơi cần dữ liệu hơn so với nguồn gốc (thường là database). Dữ liệu được giữ trong bộ nhớ RAM thay vì ổ đĩa, nên tốc độ truy xuất chênh lệch rất lớn:

  • Đọc từ cache in-memory (như Redis, Memcached): thường dưới 1 mili-giây.
  • Đọc từ database có truy vấn phức tạp: 10–100 mili-giây, có khi hơn nếu phải join nhiều bảng.
Ba lợi ích chính của cache mà bạn cần ghi nhớ để giải thích cho stakeholder:

  • Tốc độ (Latency): Người dùng thấy trang tải nhanh hơn, trải nghiệm mượt hơn. Mỗi 100ms chậm đi có thể làm giảm tỷ lệ chuyển đổi.
  • Chi phí (Cost): Giảm số lần gọi vào database nghĩa là database làm việc nhẹ hơn, không cần nâng cấp server đắt tiền. Đây là lập luận tài chính bạn có thể đưa ra trong business case.
  • Khả năng chịu lỗi (Resilience): Khi database quá tải hoặc tạm sập, cache vẫn có thể phục vụ dữ liệu cũ, giúp hệ thống "xuống cấp nhẹ nhàng" (graceful degradation) thay vì sập hoàn toàn.

Cache hit và cache miss

Hai thuật ngữ bạn sẽ nghe liên tục:

  • Cache hit: Dữ liệu cần tìm đã có sẵn trong cache → trả về ngay, không cần đụng database.
  • Cache miss: Dữ liệu không có trong cache → phải đi vào database lấy, rồi (thường là) lưu lại vào cache cho lần sau.
Tỷ lệ cache hit ratio (số hit / tổng số request) là một chỉ số sức khỏe quan trọng. Hit ratio 90% nghĩa là cứ 100 request thì 90 được phục vụ từ cache. Là BA, đây là một metric bạn nên đưa vào yêu cầu phi chức năng (non-functional requirement) khi cần.

TTL — Time To Live

TTL là khoảng thời gian một mục dữ liệu được phép "sống" trong cache trước khi bị coi là hết hạn và xóa đi. Ví dụ, bạn đặt TTL = 60 giây cho danh sách sản phẩm bán chạy. Trong 60 giây đó, mọi request đều được phục vụ từ cache. Sau 60 giây, mục đó hết hạn; request tiếp theo sẽ là cache miss, hệ thống tính lại từ database và nạp bản mới với TTL mới.

TTL là cách đơn giản và phổ biến nhất để quản lý "độ tươi" của dữ liệu. Câu hỏi nghiệp vụ cốt lõi mà bạn phải trả lời là: "Dữ liệu này cũ tối đa bao lâu thì người dùng vẫn chấp nhận được?"

  • Tỷ giá ngoại tệ trên app ngân hàng: TTL ngắn, có thể vài giây đến một phút.
  • Danh mục ngành hàng trên sàn TMĐT: TTL dài, có thể vài giờ vì hiếm khi đổi.
  • Thông tin hồ sơ người dùng (tên, ảnh đại diện): TTL trung bình, vài phút.
Đánh đổi (trade-off) của TTL: TTL càng dài thì cache hit ratio càng cao, database càng nhẹ — nhưng nguy cơ phục vụ dữ liệu cũ (stale data) càng lớn. Tìm điểm cân bằng này chính là phần việc của BA cùng product owner.

Write-through — ghi xuyên qua

Cho đến giờ ta nói về cache cho việc đọc. Nhưng còn khi dữ liệu thay đổi thì sao? Đây là lúc các chiến lược ghi (write strategy) xuất hiện.

Write-through nghĩa là: mỗi khi có thao tác ghi (cập nhật dữ liệu), hệ thống ghi đồng thời vào cả cache database, trong cùng một thao tác, trước khi báo "thành công" cho người dùng.

Hình dung như viết một tờ giấy và lập tức photo ngay một bản: bản gốc (database) và bản photo (cache) luôn khớp nhau.

  • Ưu điểm: Cache và database luôn nhất quán. Không bao giờ có chuyện cache giữ dữ liệu cũ hơn database. Dữ liệu vừa ghi xong là đọc lại được ngay từ cache.
  • Nhược điểm: Mỗi thao tác ghi chậm hơn một chút vì phải ghi hai nơi. Nếu nghiệp vụ ghi nhiều, độ trễ này cộng dồn lại đáng kể.
Write-through phù hợp khi tính nhất quán quan trọng hơn tốc độ ghi — ví dụ thông tin số dư tài khoản, trạng thái đơn hàng.

Write-behind (Write-back) — ghi trễ

Write-behind (còn gọi write-back) nghĩa là: khi có thao tác ghi, hệ thống chỉ ghi vào cache trước và báo "thành công" ngay cho người dùng. Việc ghi xuống database được làm sau đó, theo lô (batch) hoặc theo định kỳ, ở chế độ nền (background).

Hình dung như một nhân viên ghi đơn hàng vào sổ tay cá nhân (cache) cho nhanh, rồi cuối giờ mới nhập hết vào hệ thống chính (database) một lần.

  • Ưu điểm: Thao tác ghi cực nhanh vì chỉ chạm vào cache in-memory. Gom nhiều lần ghi thành một lô giúp giảm tải database mạnh mẽ. Lý tưởng cho khối lượng ghi rất lớn (ví dụ đếm lượt xem, đếm like).
  • Nhược điểm: Rủi ro mất dữ liệu. Nếu cache sập trước khi kịp ghi xuống database, những thay đổi chưa được lưu sẽ biến mất. Đây là đánh đổi nghiêm trọng cần được stakeholder hiểu rõ.
Write-behind phù hợp khi tốc độ ghi và khả năng chịu tải quan trọng hơn việc không được mất một vài bản ghi — ví dụ đếm số lượt xem video, ghi log hành vi người dùng. Tuyệt đối không dùng cho dữ liệu tài chính nếu chưa có cơ chế bảo vệ bổ sung.

Bảng so sánh nhanh để bạn ghi nhớ

Tiêu chíTTL (chủ yếu cho đọc)Write-throughWrite-behind
Mục tiêu chínhQuản lý độ tươi dữ liệu đọcNhất quán cache–DBTốc độ ghi tối đa
Tốc độ ghiKhông liên quan trực tiếpChậm hơn (ghi 2 nơi)Rất nhanh (ghi cache trước)
Nguy cơ dữ liệu cũCó (trong khoảng TTL)ThấpThấp với người đọc, nhưng DB trễ
Nguy cơ mất dữ liệuKhôngKhôngCó (nếu cache sập)
Hợp vớiDữ liệu đọc nhiều, đổi chậmSố dư, trạng thái đơnĐếm view, like, log

Tình huống thực tế

Tình huống 1: Tiki và trang chi tiết sản phẩm trong ngày sale 11/11

Bối cảnh: Một sàn TMĐT lớn như Tiki, vào ngày sale 11/11, có những sản phẩm hot được xem hàng trăm nghìn lần mỗi giờ. Trang chi tiết sản phẩm cần hiển thị tên, mô tả, ảnh, giá và tồn kho. Nếu mỗi lượt xem đều truy vấn database, hệ thống sẽ sập trong vài phút.

Diễn giải: Team áp dụng cache với TTL cho phần thông tin ít đổi (tên, mô tả, ảnh) — đặt TTL khá dài, ví dụ 5 phút, vì những thứ này gần như không thay đổi trong phiên sale. Nhưng giá và tồn kho là chuyện khác: giá sale có thể đổi, tồn kho giảm theo từng giây. Nếu cache giá quá lâu, khách có thể đặt mua với giá đã hết hiệu lực hoặc mua sản phẩm đã hết hàng — gây khiếu nại và hủy đơn hàng loạt. Vì vậy team đặt TTL rất ngắn cho tồn kho (vài giây) hoặc dùng chiến lược cập nhật chủ động khi có giao dịch.

Bài học rút ra: Không phải mọi trường dữ liệu trong cùng một màn hình đều nên có cùng TTL. Là BA, khi viết spec, bạn nên phân loại từng trường dữ liệu theo mức độ "nhạy cảm với độ tươi" và đề xuất TTL tương ứng cho từng nhóm. Câu hỏi vàng: "Nếu khách thấy con số này cũ 30 giây, điều tệ nhất xảy ra là gì?"

Tình huống 2: Ví điện tử MoMo và số dư tài khoản

Bối cảnh: Một ví điện tử như MoMo phải hiển thị số dư chính xác tuyệt đối. Khi người dùng nạp 200.000đ, họ phải thấy số dư tăng ngay lập tức. Nếu họ refresh và vẫn thấy số dư cũ vì cache chưa cập nhật, niềm tin vào ví sẽ sụp đổ — đây là rủi ro chết người với một sản phẩm tài chính.

Diễn giải: Ở đây, write-through là lựa chọn hợp lý. Mỗi khi số dư thay đổi (nạp tiền, chuyển khoản, thanh toán), hệ thống ghi đồng thời vào database và cập nhật cache trong cùng giao dịch. Nhờ đó, ngay khoảnh khắc giao dịch hoàn tất, cả database lẫn cache đều phản ánh số dư mới. Người dùng đọc lại thấy ngay con số đúng. Tốc độ ghi chậm hơn một chút là cái giá hoàn toàn chấp nhận được để đổi lấy sự nhất quán tuyệt đối về tiền bạc.

Bài học rút ra: Với dữ liệu tài chính và những con số người dùng "soi" từng đồng, hãy ưu tiên nhất quán hơn tốc độ. Là BA, khi yêu cầu đụng đến tiền, điểm tồn kho, hay trạng thái pháp lý, bạn nên ghi rõ trong acceptance criteria rằng "dữ liệu phải nhất quán ngay sau khi ghi" — đây là tín hiệu để team kỹ thuật chọn write-through chứ không phải write-behind.

Tình huống 3: Một startup video Đông Nam Á và bộ đếm lượt xem

Bối cảnh: Một nền tảng video kiểu TikTok ở Đông Nam Á cần đếm lượt xem cho mỗi video. Một video viral có thể nhận 50.000 lượt xem mỗi phút. Nếu mỗi lượt xem là một lần ghi vào database (UPDATE views = views + 1), database sẽ nghẽn cổ chai vì hàng chục nghìn thao tác ghi cạnh tranh trên cùng một dòng.

Diễn giải: Team chọn write-behind. Mỗi lượt xem chỉ tăng bộ đếm trong cache (Redis) — cực nhanh. Cứ mỗi 10 giây, một tiến trình nền gom tổng số lượt tăng và ghi xuống database một lần duy nhất. Thay vì 500.000 thao tác ghi trong 10 giây, database chỉ nhận 1 thao tác. Họ chấp nhận rủi ro: nếu Redis sập đúng lúc, có thể mất vài giây đếm view. Nhưng "lượt xem hiển thị thiếu vài chục" là thiệt hại không đáng kể so với việc database sập làm sập cả app.

Bài học rút ra: Write-behind tỏa sáng khi khối lượng ghi khổng lồ và mất mát nhỏ có thể chấp nhận được. Là BA, bạn cần giúp stakeholder nhìn rõ đánh đổi này một cách định lượng: "Chúng ta đổi lấy việc tải nhẹ hơn 99% cho database, với rủi ro mất tối đa 10 giây dữ liệu đếm trong trường hợp hiếm gặp cache sập. Bạn có chấp nhận không?" Khi đặt câu hỏi rõ ràng như vậy, quyết định trở nên dễ dàng.

Hướng dẫn từng bước

Khi bạn đối mặt với một yêu cầu mà caching có thể là giải pháp, hãy đi theo các bước sau:

  • Xác định dữ liệu nào đọc nhiều. Nhìn vào các màn hình và API được gọi thường xuyên nhất. Cache chỉ đáng giá khi tỷ lệ đọc cao hơn nhiều so với ghi. Dữ liệu đọc một lần rồi thôi thì cache vô ích.
  • Đánh giá tần suất thay đổi của dữ liệu. Dữ liệu càng ít đổi càng dễ cache với TTL dài. Liệt kê từng trường: "đổi mỗi giây", "đổi mỗi giờ", "gần như không đổi".
  • Trả lời câu hỏi vàng về độ tươi. Với mỗi dữ liệu: "Người dùng chấp nhận thấy nó cũ tối đa bao lâu?" Câu trả lời chính là TTL đề xuất.
  • Phân loại theo thao tác chủ đạo — đọc hay ghi. Nếu nghiệp vụ chủ yếu là đọc, tập trung vào TTL. Nếu có nhiều thao tác ghi quan trọng, cân nhắc write-through (cần nhất quán) hay write-behind (cần tốc độ).
  • Cân nhắc hậu quả của dữ liệu cũ và mất dữ liệu. Viết ra rõ ràng: điều tệ nhất xảy ra nếu khách thấy dữ liệu cũ? Điều tệ nhất nếu mất một vài bản ghi ghi gần đây? Đây là đầu vào để chọn chiến lược.
  • Ghi vào yêu cầu phi chức năng. Đưa các quyết định trên vào spec dưới dạng cụ thể: "Danh mục sản phẩm cache với TTL 1 giờ", "Số dư ví dùng write-through, nhất quán ngay sau ghi", "Bộ đếm view dùng write-behind, đồng bộ DB mỗi 10 giây, chấp nhận mất tối đa 10 giây dữ liệu khi sự cố".
  • Thống nhất chỉ số đo lường. Đề xuất theo dõi cache hit ratio và độ trễ. Đặt mục tiêu, ví dụ "hit ratio tối thiểu 85% sau 1 tuần vận hành".

Lỗi thường gặp & mẹo

Lỗi 1 — Cache mọi thứ một cách mù quáng. Không phải dữ liệu nào cũng nên cache. Dữ liệu cá nhân hóa cao (ví dụ giỏ hàng riêng của từng người) hoặc dữ liệu thay đổi liên tục có thể khiến cache vô dụng hoặc thậm chí nguy hiểm. Mẹo: chỉ cache thứ được đọc nhiều và đổi chậm.

Lỗi 2 — Quên xử lý "cache invalidation". Có một câu nói nổi tiếng trong giới kỹ thuật: "Hai việc khó nhất trong khoa học máy tính là đặt tên biến và làm mới cache (cache invalidation)." Khi dữ liệu nguồn đổi nhưng cache vẫn giữ bản cũ và chưa hết TTL, người dùng thấy thông tin sai. Mẹo: với BA, hãy luôn hỏi team "khi dữ liệu này đổi, cache được làm mới bằng cách nào?" — bằng hết TTL, hay chủ động xóa cache khi ghi?

Lỗi 3 — Nhầm lẫn write-through và write-behind khi viết spec. Hai cái nghe giống nhau nhưng đánh đổi ngược nhau hoàn toàn (nhất quán vs tốc độ, an toàn vs rủi ro mất dữ liệu). Mẹo: nhớ một câu — write-through là "ghi cả hai nơi rồi mới báo xong", write-behind là "báo xong trước, ghi DB sau".

Lỗi 4 — Đặt TTL theo cảm tính, không gắn với nghiệp vụ. TTL không phải con số kỹ thuật ngẫu nhiên; nó là quyết định nghiệp vụ về độ tươi chấp nhận được. Mẹo: luôn diễn giải TTL thành ngôn ngữ nghiệp vụ trước khi chốt số.

Lỗi 5 — Bỏ qua kịch bản "thundering herd". Khi một mục cache hot hết hạn cùng lúc, hàng nghìn request đồng loạt dội vào database để tính lại — gây quá tải đột ngột. Mẹo: bạn không cần giải bài toán này, nhưng nên biết để nhắc team trong các tình huống lưu lượng cao như sale 11/11, 12/12.

Mẹo chung: Khi nghi ngờ, mặc định ưu tiên sự nhất quán cho dữ liệu nhạy cảm (tiền, tồn kho, pháp lý) và ưu tiên tốc độ cho dữ liệu "vui là chính" (đếm view, like, gợi ý). Đặt câu hỏi "điều tệ nhất là gì?" sẽ luôn dẫn bạn đến quyết định đúng.

Bài tập thực hành

  • Phân loại TTL: Cho một app đặt đồ ăn (kiểu ShopeeFood/GrabFood), hãy liệt kê 5 loại dữ liệu (ví dụ: danh sách nhà hàng, menu món, trạng thái mở/đóng cửa, giá món, vị trí shipper). Với mỗi loại, đề xuất một TTL và viết một câu giải thích lý do nghiệp vụ.
  • Chọn chiến lược ghi: Cho ba tình huống sau, hãy chọn write-through hay write-behind và giải thích đánh đổi: (a) cập nhật điểm tích lũy thành viên sau mỗi đơn hàng; (b) đếm số lần một bài viết được chia sẻ; (c) cập nhật trạng thái "đang giao" của đơn hàng.
  • Viết yêu cầu phi chức năng: Soạn 3 dòng acceptance criteria liên quan đến caching cho tính năng "trang chủ hiển thị sản phẩm nổi bật", bao gồm TTL, hành vi khi cache miss, và mục tiêu cache hit ratio.
  • Phân tích rủi ro: Một product owner đề xuất dùng write-behind cho số dư ví điện tử để app nhanh hơn. Viết một đoạn ngắn (3–4 câu) giải thích vì sao đây là quyết định rủi ro và bạn sẽ phản biện thế nào.

Tóm tắt

Caching là một lớp lưu trữ tạm thời, nhanh, đặt gần người dùng, giúp tăng tốc độ, giảm chi phí database và tăng khả năng chịu lỗi của hệ thống. Là BA, bạn không viết code cache, nhưng bạn là người quyết định những đánh đổi nghiệp vụ ẩn sau nó.

Ba chiến lược cốt lõi cần nhớ: TTL quản lý độ tươi của dữ liệu đọc — TTL dài thì nhanh và rẻ nhưng dễ cũ; câu hỏi vàng là "dữ liệu cũ tối đa bao lâu thì chấp nhận được?". Write-through ghi đồng thời vào cache và database, đảm bảo nhất quán tuyệt đối, đổi lại ghi chậm hơn — lý tưởng cho tiền và trạng thái quan trọng. Write-behind ghi vào cache trước rồi đồng bộ database sau, cho tốc độ ghi cực cao nhưng có rủi ro mất dữ liệu khi cache sập — lý tưởng cho đếm view, like, log.

Kỹ năng quan trọng nhất bạn mang về từ bài này không phải là thuật ngữ, mà là thói quen đặt câu hỏi "điều tệ nhất xảy ra là gì?" cho mỗi mẩu dữ liệu, rồi diễn giải câu trả lời đó thành TTL và chiến lược ghi cụ thể trong spec. Đó chính là lúc bạn chuyển từ một BA "biết khái niệm" thành một BA "thiết kế được giải pháp".