Cách xây dựng hệ thống có khả năng mở rộng cho người mới bắt đầu – Ngày 3: Cache

Bài viết như một ghi chú ghi khi tác giả tìm hiểu về xây dựng hệ thống có khả năng mở rộng (Scalability).

Bài viết được dịch từ link: Scalability for Dummies – Part 3: Cache

Caching

Qua Phần 2 của series, chúng ta đã có một cơ sở dữ liệu với khả năng mở rộng. Mọi chuyện vẫn ổn cho dùng bạn có hàng terabyte dữ liệu phải lưu trữ. Nhưng nó chỉ ổn cho mình bạn. Những người dùng hệ thống của bạn sẽ gặp tình trạng trang web tải bị chậm khi có nhiều dữ liệu cần được đọc từ cơ sở dữ liệu. Giải pháp cho vấn đề này là triển khai bộ nhớ Cache cho ứng dụng.

Trong bài viết này và trong những trường hợp thông thường, “Cache” được hiểu là in-memory cache. Ví dụ: Memcached or Redis. Nếu không có yêu cầu đặc biệt, hãy nhớ đừng bao giờ sử dụng file-base cache, nó này sẽ làm cho việc cloning và auto-scale server trở nên rất khó khăn.

Trở lại với in-memory cache. Một cache service có thể đơn giản là một bộ lưu trữ key-value, và nó phải là lớp đệm nằm giữa tầng ứng dụng và tầng cơ sở dữ liệu. Mỗi khi ứng dụng cần đọc dữ liệu, trước tiên, hãy cố gắng lấy dữ liệu từ bộ nhớ cache. Chỉ khi dữ liệu cần lấy không tồn tại trong bộ nhớ cache, chúng ta sẽ lấy dữ liệu đó từ nguồn dữ liệu chính (cơ sở dữ liệu). Tại sao chúng ta nên làm như thế? Bởi vì bộ nhớ cache có tốc độ truy xuất dữ liệu cực nhanh. Nó lưu trữ dữ liệu trên bộ nhớ RAM và về mặt kỹ thuật các yêu cầu sẽ được xử lý nhanh nhất có thể. Ví dụ, Redis có thể xử được hàng trăm ngàn phép đọc dữ liệu mỗi giây khi nó được cài đặt trên một server thông thường. Tương tự cho phép ghi dữ liệu, đặc biệt với phép gia số (increment), cực kỳ nhanh. Hãy thử với một cơ sở dữ liệu thông thường!

Chúng ta có 2 kiểu lưu trữ dữ liệu trong cache: Kiểu cũ(#1) và Kiểu mới(#2)

#1 Lưu trữ kết quả của các truy vấn cơ sở dữ liệu trong cache

Đây vẫn là cách dùng phổ biến nhất khi sử dụng cache. Mỗi khi bạn thực hiện một truy vấn tới cơ sở dữ liệu, bạn sẽ lưu kết quả truy vấn vào cache. Giá trị khi hash câu truy vấn sẽ được sử dụng như khóa của bộ nhớ cache. Ở lần tiếp theo theo khi bạn cần thực hiện truy vấn đó, trước tiên bạn phải kiểm tra xem nó đã tồn tại trong cache hay chưa. Cách này có một số vấn đề. Trong đó vấn đề chính là thời gian hiệu lực của giá trị trong cache. Sẽ là rất phức tạp để xóa đi dữ liệu đã được cache cho một câu truy vấn phức tạp (Ai mà không có những câu truy vấn phức tạp?). Khi một phần của dữ liệu thay đổi(ví dụ: một ô dữ liệu trong bảng), bạn phải xóa toàn bộ giá trị đã lưu cho tất cả các truy vấn có thể liên quan tới bảng (hay ô) dữ liệu đó. Bạn đã nhận ra chưa?

#2 Lưu trữ đối tượng

Đây là cách mà tôi đặc biệt khuyến khích và tôi luôn ưu tiên dùng cách này. Nhìn chung, coi dữ liệu của bạn như những đối tượng mà bạn đã định nghĩa trong code (classes, instances, …). Hãy để các lớp thu thập đủ dữ liệu từ trong cơ sở dữ liệu và sau đó lưu trữ phiên bản hoàn hình của lớp đó hoặc tập hợp dữ liệu trong bộ nhớ cache. Bạn thấy có vẻ hơi lý thuyết, nhưng hãy nhìn vào đoạn code bình thường của bạn. Bạn sẽ có những thứ như, lớp “Product” – Lớp có một thuộc tính “data”. Thuộc tính đó là một mảng chứa tập các thông tin giá cả, mô tả, hình ảnh, và các đánh giá của sản phẩm. Giá trị thuộc tính “data” được thu thập bằng nhiều phương thức của lớp Product, thực hiện nhiều truy vấn tới cơ sở dữ liệu – Những thứ khó để lưu trong cache vì nhiều thứ có liên quan tới nhau. Bây giờ, chúng ta sẽ làm theo các bước: Khi lớp của bạn đã hoàn thành việc “tập hợp” dữ liệu cho thuộc tính “data”, hãy lưu trữ trực tiếp mảng dữ liệu đó trong cache, hoặc tốt hơn là lưu trữ toàn bộ lớp đó trong cache! Cách này cho phép bạn dễ dàng loại bỏ đỗi tượng đã lưu trữ bất cứ khi nào khi có gì đó thay đổi trong cơ sở dữ liệu và cách này sẽ làm cho code của bạn nhanh hơn và hợp lý hơn.

Và phần tốt nhất: Hãy làm nó bất đồng bộ nhất có thể! Hãy tưởng tượng bạn có một đội quân là các máy chủ, các công nhân đang lắp ráp các đồ vật cho bạn ?!? Ứng dụng của bạn sẽ chỉ sử dụng những dữ liệu đã được lưu trong cache mới nhất và gần như không bao giờ phải truy vấn tới cơ sở dữ liệu!

Một vài ý tưởng cho việc lưu trữ đối tượng trong cache:

  • Phiên làm việc của người dùng(đừng bao giờ sử dụng cơ sở dữ liệu!)
  • Các trang bài viết của blog đã được kết xuất đầy đủ
  • Luồng hoạt động(Active stream) – Ví dụ: Tập hợp các đoạn chat, ghi chú của một nghiệp vụ cụ thể
  • Mối quan hệ bạn bè của người dùng

Có thể bạn đã nhận ra, tôi là một fan cuồng của caching. Nó không khó để hiểu, đơn giản để triển khai và kết quả luôn luôn rất ngoạn mục. Nhìn chung, tôi thích Redis hơn Memcached, vì tôi thích những tính năng của Redis như tính bền bỉ và cấu trúc dữ liệu list và set được hỗ trợ sẵn. Với Redis và cách đặt key thông minh bạn có khả năng loại bỏ cơ sở dữ liệu hoàn toàn. Nhưng nếu thứ bạn cần đơn giản chỉ là cache thì Memcached là một lựa chọn, bởi vì nó có thể được mở rộng một cách tuyệt vời.

Happy caching!