Best Practice


1. Chiến lược: Proactive Cache Warming (Làm nóng Cache chủ động qua Event-Driven)

Mô tả của bạn: "Mỗi lần insert mới a lắng nghe log để insert vào caching lun thông qua cơ chế pub sub"

Thay vì sử dụng mô hình Cache-Aside truyền thống (Code phải chờ DB insert xong rồi mới tự tay insert vào Cache, làm tăng latency của request chính), bạn chuyển sang Event-Driven Architecture.

  • Cơ chế hoạt động:

    1. Service thực hiện ghi dữ liệu vào Database chính (Primary DB).

    2. Một sự kiện (Event) được phát ra ngay sau đó (thông qua Message Queue như Kafka/RabbitMQ hoặc Change Data Capture - CDC từ Binlog DB).

    3. Một Worker/Consumer riêng biệt lắng nghe sự kiện này và thực hiện việc cập nhật/insert dữ liệu vào Redis.

  • Lợi ích kỹ thuật:

    • Decoupling (Giảm sự phụ thuộc): Luồng xử lý chính (Main Thread) trả phản hồi cho user ngay sau khi ghi DB xong, không bị block bởi thao tác ghi Redis.

    • High Availability: Nếu Redis bị nghẽn hoặc chết, luồng chính của user không bị ảnh hưởng (không bị lỗi 500).

    • Eventual Consistency: Đảm bảo cache luôn tươi mới gần như tức thì (near real-time) mà không cần chờ user tiếp theo query mới set cache.

2. Chiến lược: Storage Optimization via Compression (Nén dữ liệu)

Mô tả của bạn: "Cái t2 mặc định a nén data trước khi bắn vào caching"

Đây là kỹ thuật đánh đổi một chút CPU (để nén/giải nén) lấy lợi ích to lớn về Memory và I/O mạng.

  • Cơ chế hoạt động:

    • Write: Trước khi SET vào Redis, Backend sử dụng thuật toán nén (thường là Snappy, LZ4, Zstd hoặc Gzip) để thu nhỏ payload.

    • Read: Khi GET từ Redis, Backend nhận về binary đã nén và giải nén trước khi sử dụng.

  • Lợi ích kỹ thuật:

    • Giảm RAM Footprint: Redis lưu trữ trên RAM (vốn đắt đỏ). Với các JSON object lớn, việc nén có thể giảm dung lượng từ 30% - 70%.

    • Tăng Throughput mạng: Payload nhỏ hơn đồng nghĩa với việc truyền tải qua mạng nội bộ nhanh hơn, giảm nghẽn băng thông (Network Bandwidth).

  • Lưu ý: Nên chọn thuật toán như Snappy hoặc LZ4 vì tốc độ cực nhanh, phù hợp cho Real-time caching hơn là Gzip (nén tốt nhưng chậm).

3. Chiến lược: Zero-Deserialization (Tối ưu CPU & GC)

Mô tả của bạn: "T3 khi get caching => a return byte tránh unmasher tối ưu memory"

Đây là kỹ thuật tối ưu cực sâu (Micro-optimization), đặc biệt hiệu quả với ngôn ngữ như Go (Golang) hoặc Java để giảm áp lực cho Garbage Collector (GC).

  • Cơ chế hoạt động:

    • Dữ liệu trong Redis được lưu trữ dưới dạng Pre-computed JSON (hoặc format mà Client cần nhận).

    • Khi Backend GET dữ liệu từ Redis, nó nhận về một mảng byte ([]byte).

    • Thay vì Unmarshal (Decode) mảng byte đó thành Object/Struct rồi lại Marshal (Encode) ra JSON để trả về Client, bạn đẩy thẳng (stream) mảng byte đó vào Response Body.

  • Lợi ích kỹ thuật:

    • Tiết kiệm CPU: Quá trình Serialization/Deserialization (JSON Marshal/Unmarshal) thường chiếm lượng lớn CPU trong các ứng dụng Backend. Bỏ qua bước này giúp giảm tải CPU đáng kể.

    • Giảm Memory Allocations: Không tạo ra các Struct/Object tạm thời, giúp giảm áp lực lên bộ nhớ Heap và giảm tần suất chạy Garbage Collection.


Tổng hợp so sánh hiệu quả

Để bạn dễ hình dung sự kết hợp của 3 cách trên:

Chiến lược

Vấn đề giải quyết

Trade-off (Đánh đổi)

Mức độ ưu tiên

1. Event-Driven Update

Latency của API ghi (Write)

Phức tạp trong setup hạ tầng (Queue, Worker).

Cao (Cho hệ thống High Traffic)

2. Compression

Chi phí hạ tầng (RAM Redis), Network I/O

Tốn thêm CPU để Nén/Giải nén.

Trung bình (Khi Data > 1KB)

3. Zero-Deserialization

CPU usage, GC pressure của Backend

Kém linh hoạt (Data trong cache phải giống hệt response client cần).

Cao (Cho API Read-heavy)

Last updated