Một trang web video có lưu lượng truy cập cao đã cắt giảm số lượng truy vấn cơ sở dữ liệu cho nguồn cấp dữ liệu trang xu hướng từ 4.000 truy vấn mỗi phút xuống dưới 50, đồng thời giảm thời gian phản hồi ở phân vị thứ 95 (95th-percentile) từ 380 ms xuống còn 40 ms bằng cách chuyển từ lưu trữ đệm toàn bộ trang (whole-page caching) sang lưu trữ đệm phân đoạn (fragment caching) với Varnish và Edge Side Includes (ESI).

Tại sao trang web cần một chiến lược bộ nhớ đệm khác

Trang chủ hiển thị các clip được xem nhiều nhất trong ngày trông gần như giống nhau đối với hầu hết khách truy cập trong một khu vực: khoảng 95% mã HTML là giống nhau đối với một triệu người dùng, trong khi 5% còn lại chứa dữ liệu cá nhân như tên người dùng đã đăng nhập hoặc hộp tìm kiếm. Đội ngũ kỹ thuật đã phải đối mặt với hai lựa chọn không mấy hấp dẫn:

  • Lưu trữ đệm toàn bộ trang và chấp nhận rủi ro hiển thị dữ liệu cá nhân cũ cho người dùng đã đăng nhập.
  • Bỏ qua hoàn toàn bộ nhớ đệm và để mọi yêu cầu gây áp lực lớn lên cơ sở dữ liệu.

Cả hai cách tiếp cận đều làm ảnh hưởng đến trải nghiệm người dùng. Nhóm đã chuyển sang sử dụng ESI, một kỹ thuật cho phép reverse-proxy lắp ráp một trang từ các phân đoạn được lưu trữ đệm độc lập tại các nút mạng biên (edge).

Cách thiết lập lưu trữ đệm phân đoạn

Varnish, trình tăng tốc HTTP mã nguồn mở, coi trang web như một khung xương với ba phần có thể thay thế:

  • Lưới video (Video grid) – danh sách các video đang thịnh hành trong toàn khu vực, vốn rất tốn tài nguyên. Được lưu trữ đệm trong 60 giây vì nó thay đổi thường xuyên nhưng giống nhau đối với mọi khách truy cập ẩn danh.
  • Bộ chuyển đổi ngôn ngữ (Language switcher) – một thành phần giao diện tĩnh hiếm khi thay đổi. Được lưu trữ đệm trong 24 giờ.
  • Header – phân đoạn thực sự mang tính cá nhân duy nhất (tên người dùng, ảnh đại diện, thông báo). Không bao giờ được lưu trữ đệm; Varnish sẽ chuyển tiếp yêu cầu đến máy chủ ứng dụng mỗi lần.

Khi một yêu cầu đến, Varnish sẽ cung cấp khung xương đã được lưu trữ đệm, lấy hai phân đoạn đã lưu trữ đệm từ kho lưu trữ cục bộ và chèn header trực tiếp từ backend vào.

Những con số quan trọng

Sau khi chuyển đổi:

  • Tải cơ sở dữ liệu cho trang xu hướng giảm từ 4.000 truy vấn mỗi phút xuống dưới 50.
  • Độ trễ ở phân vị thứ 95 giảm từ 380 ms xuống 40 ms.

Ba bài học thực tế từ quá trình triển khai

1. Thời gian ân hạn (Grace periods) giúp làm mượt các lần cache miss Khi TTL của một phân đoạn hết hạn, Varnish thông thường sẽ tạm dừng để lấy nội dung mới, tạo ra một sự tăng vọt về độ trễ có thể dẫn đến hiện tượng "thundering herd" (hàng loạt yêu cầu đồng thời) gửi đến backend. Bằng cách cấu hình thời gian ân hạn (grace period), Varnish sẽ tiếp tục cung cấp phân đoạn cũ trong khi âm thầm làm mới bộ nhớ đệm ở chế độ nền. Người dùng không thấy sự gián đoạn; backend thấy một tỷ lệ yêu cầu ổn định và có thể kiểm soát được.

2. Loại bỏ cookie cho các phân đoạn ẩn danh Cookie đính kèm vào mỗi yêu cầu khiến Varnish coi mỗi yêu cầu là duy nhất, làm mất hiệu lực các lần trúng bộ nhớ đệm (cache hits). Nhóm đã loại bỏ cookie cho lưới video và bộ chuyển đổi ngôn ngữ, cho phép các phân đoạn đó được lưu trữ đệm một cách triệt để. Chỉ có phân đoạn header là mang theo cookie, giúp duy trì tính cá nhân hóa mà không làm mất hiệu quả của bộ nhớ đệm.

3. Khóa surrogate (Surrogate keys) cho phép xóa bỏ tức thì Đôi khi một video phải được gỡ bỏ ngay lập tức—ví dụ: vì lý do bản quyền. Việc chờ đợi TTL 60 giây hết hạn là không thể chấp nhận được. Bằng cách gắn thẻ mỗi phân đoạn được lưu trữ đệm với một khóa surrogate phản ánh các ID video tương ứng, nhóm có thể phát ra một lệnh xóa (purge) duy nhất để vô hiệu hóa ngay lập tức tất cả các bản sao của một video cụ thể trên mọi nút mạng biên. Điều này giúp tránh việc phải quét toàn bộ bộ nhớ đệm và giúp trang web luôn tuân thủ quy định.

Kết luận: Lưu trữ đệm phân đoạn với Varnish và ESI biến một trang web nguyên khối, phụ thuộc vào cơ sở dữ liệu thành một tập hợp các thành phần nhẹ và có thể tái sử dụng, giúp cắt giảm tải trọng backend và độ trễ trong khi vẫn duy trì tính cá nhân hóa cho từng người dùng.