Quay lại bài viết
30 thg 3, 2026
8 min read

Cache Handbook: Từ Monitoring đến Scaling - Vận hành hệ thống Cache “triệu request”

Bài viết được tổng hợp và biên soạn lại từ cuốn “A Cache Handbook for Software Engineers” của Quang Hoang (Software Engineer tại Google). Đây là phần 4/4 trong series.

Ở các phần trước, chúng ta đã tìm hiểu nền tảng caching, bài toán consistencycác cái bẫy thường gặp. Bài viết cuối cùng này sẽ tập trung vào hai khía cạnh vận hành quan trọng nhất: Monitoring (giám sát) và Scaling (mở rộng) hệ thống Cache.


Phần 1: Monitoring Cache

Để vận hành Cache hiệu quả, bạn cần theo dõi các chỉ số (metric) quan trọng sau.

1.1. Hit Rate

Nhắc lại công thức:

Hit Rate = Cache Hit / (Cache Hit + Cache Miss) × 100

Chỉ số này thường được đo theo nhóm chức năng hoặc API, ví dụ Hit Rate của GetProduct API.

Không có con số tuyệt đối, nhưng với các hệ thống thông thường:

1.2. Latency

Lý do tồn tại quan trọng nhất của Cache là tốc độ, nên cần monitor:

1.3. Throughput

Throughput được đo bằng Ops/sec (Operations per second — số lượng lệnh thực thi trên mỗi giây). Monitor chỉ số này giúp bạn xác định giới hạn của hệ thống và đưa ra quyết định Scale-out.

1.4. Memory

Fragmentation Ratio = Memory Allocated by OS / Memory Used by Data

Hai mốc quan trọng:

1.5. Connection Churn Rate

Để phát hiện Connection Churn, không nhìn vào số kết nối đang mở (Connected Clients), mà phải nhìn vào tốc độ tạo kết nối mới. Chỉ số này được tính bằng đạo hàm bậc 1 của số lượng kết nối. Ví dụ trong Prometheus:

rate(connection_totals)

Phần 2: Scaling Cache

Khi lưu lượng truy cập và kích thước dữ liệu quá lớn, một Cache server thường không đủ. Việc mở rộng theo chiều dọc (Vertical Scaling — tăng RAM, CPU) thường gặp rào cản chi phí và bottleneck I/O mạng. Giải pháp tối ưu là Horizontal Scaling.

Câu hỏi quan trọng: Làm thế nào để phân phối các key lên N server một cách đồng đều và ổn định?

2.1. Modulo Hashing

Cách tiếp cận cơ bản nhất: mỗi key được hash ra một số nguyên, sau đó chia lấy dư cho tổng số node (N):

Node Index = hash(key) mod N

Vấn đề Rebalancing: Khi N thay đổi (thêm/bớt node), phép chia thay đổi mẫu số → gần như toàn bộ key bị tính sai vị trí. Tỷ lệ key phải di chuyển xấp xỉ N/(N+1).

Ví dụ: Cụm 10 nodes, thêm 1 node mới → khoảng 90% dữ liệu bị Cache Miss diện rộng (Cache Avalanche), đẩy toàn bộ tải xuống Database.

2.2. Consistent Hashing

Được giới thiệu năm 1997 bởi David Karger, Daniel Lewin, Thomson Leighton (cofounders của Akamai Technologies).

Cơ chế hoạt động:

Ưu điểm: Khi thêm/bớt node, số key bị rebalance chỉ là K/N (K = tổng số key), thay vì 90% như Modulo.

Vấn đề phân phối không đều: Nếu N nhỏ, khoảng cách giữa các node trên vòng tròn rất lớn → một node phải ôm dữ liệu nhiều hơn. Giải pháp: Virtual Nodes — một node vật lý được ánh xạ thành nhiều virtual nodes bằng cách hash IP kết hợp hậu tố (NodeA#1, NodeA#2).

Triển khai Consistent Hashing

Tùy kiến trúc, Consistent Hashing được lưu ở một trong ba vị trí:

  1. Thick Client (Client-side Routing): Lưu trực tiếp trong bộ nhớ App Server. Các thư viện như spymemcached (Java), gomemcache (Golang) tự duy trì cấu trúc này.
  2. Proxy (Middleware Routing): Lưu trên Proxy server trung gian (Twemproxy, Envoy). App Server gửi request đến Proxy, Proxy điều hướng đến đúng node.
  3. Cluster (Server-side Routing): Mỗi node trong cụm tự lưu trữ bản sao Consistent Hashing (như Cassandra, DynamoDB).

Xử lý khi node bị down

Passive Eviction: Client tính toán ra Node A, gửi request, bị timeout. Khi lỗi chạm ngưỡng (max_failures = 3), Client tự loại Node A ra khỏi Consistent Hashing. Request tiếp theo rơi vào node kế tiếp trên Ring.

Nhược điểm: Dễ gây “Split-brain” — Client 1 thấy Node A chết nhưng Client 2 vẫn thấy sống. Hai client có Consistent Hashing khác nhau, phá vỡ tính nhất quán.

Active Eviction: Sử dụng Service Discovery (Consul):

  1. Node Cache khởi động → đăng ký IP/Port lên Service Discovery.
  2. Node liên tục gửi Heartbeat → “I am alive!”
  3. Nếu Node A sập, Service Discovery ghi nhận sự kiện.
  4. Service Discovery đẩy sự kiện tới Proxy/App Server qua API hoặc HTTP Long-polling.
  5. Tất cả đồng loạt cập nhật Consistent Hashing.

2.3. Hash Slot (Redis Cluster)

Mặc dù Consistent Hashing giải quyết tốt bài toán phân tán, nó đặt gánh nặng tính toán lên Client/Proxy. Redis Cluster (từ phiên bản 3.0) tiếp cận theo hướng khác: Hash Slot.

Cơ chế hoạt động:

Redis Cluster chia key space thành 16384 slot (2¹⁴). Con số này cố định, không phụ thuộc số node. Khi key được ghi:

Hash Slot = CRC16(key) mod 16384

Mỗi node quản lý một dải Hash Slot. Ví dụ với 3 node:

Nếu Node A có RAM gấp đôi Node B, bạn có thể chủ động gán số lượng Slot cho Node A nhiều gấp đôi — thay vì phụ thuộc vào xác suất ngẫu nhiên của Virtual Nodes trong Consistent Hashing.

Tại sao là 16384? Mỗi gói tin heartbeat cần đính kèm bitmap thông tin các slot đang quản lý. Với 16384 slot: bitmap chỉ chiếm 16384 / 8 = 2048 bytes (2KB) — tối ưu cho network.

Hash Tag

Redis hỗ trợ thao tác trên nhiều key cùng lúc (MGET, MSET). Nếu key có chứa cặp ngoặc {}, hàm CRC16 chỉ tính trên giá trị bên trong:

Chuỗi trong {} đều là 100 → cả hai key rơi vào cùng một Hash Slot → cùng một Node → Client chỉ cần gửi request đến một Node.

Routing Mechanism

Khi Client muốn đọc/ghi key:

  1. Client tính Hash Slot, tra bảng mapping nội bộ → gửi request tới Node tương ứng.
  2. Nếu bảng mapping outdated, request bị lạc sang Node sai. Node trả về lỗi MOVED <slot_id> <ip_đích>:<port>.
  3. Client nhận MOVED → gửi lại request đến IP đích + cập nhật bảng mapping để lần sau đi đúng ngay từ đầu.

Gossip Protocol

Để các node trong Redis Cluster biết chính xác Hash Slot nào thuộc Node nào mà không cần server trung tâm (ZooKeeper, Consul), chúng sử dụng Gossip Protocol:

Nhờ trao đổi chéo liên tục, thông tin cấu hình “lây lan” ra toàn cụm chỉ trong vài giây.

Nhược điểm: Rào cản quy mô: tối đa 1000 node. Khi số node tăng, lượng PING/PONG tăng theo cấp số nhân. Ước tính: cluster 1000 node gửi 133,200 API gossip mỗi giây, tiêu tốn lượng lớn CPU và băng thông. Ở quy mô hyperscale, người ta ưu tiên dùng server trung tâm để quản lý trạng thái Cluster.

Zero Downtime Resharding

Khi cần thêm/bớt Node, các Hash Slot + dữ liệu phải được dời đi mà không gây gián đoạn dịch vụ.

Ví dụ: Slot 8 đang chuyển từ Node A sang Node B:

  1. Node A đánh dấu Slot 8 là MIGRATING. Node B đánh dấu IMPORTING.
  2. Background thread chuyển dần các key thuộc Slot 8 sang Node B.
  3. Nếu Client yêu cầu key thuộc Slot 8 và gửi đến Node A:
    • Key chưa chuyển đi: Trả về kết quả bình thường.
    • Key đã chuyển đi: Node A trả về lỗi ASK 8 <ip_Node_B>:<port>. Client mở kết nối tạm thời tới Node B để lấy dữ liệu (không cập nhật mapping config).

Lời kết

Qua 4 bài viết trong series này, hy vọng bạn đã gạt bỏ được ảo tưởng phổ biến: “Hệ thống chậm ư? Cứ gắn thêm Redis vào là xong.”

Sự thật là, thêm một lớp Cache không bao giờ làm hệ thống đơn giản hơn. Nó chỉ chuyển độ phức tạp từ nơi này sang nơi khác. Hy vọng series nhỏ này có thể giúp bạn nhìn thấu sự phức tạp đó để tự tin làm chủ hệ thống của mình.

Credit: Toàn bộ series được tổng hợp từ cuốn “A Cache Handbook for Software Engineers” của Quang Hoang — Software Engineer tại Google, trước đó từng làm việc tại Shopee và Rakuten. Bạn có thể kết nối với tác giả qua LinkedIn  hoặc Blog .


Series: Cache Handbook

  1. Nền tảng cốt lõi của Caching
  2. Giải mã bài toán Cache Consistency
  3. 6 “cái bẫy” kinh điển khi dùng Cache
  4. Từ Monitoring đến Scaling ← Bạn đang ở đây

Liên quan