Sản phẩm
Netflix tối ưu hóa độ trễ truy vấn Cassandra từ giây xuống mili giây nhờ kỹ thuật phân tách dữ liệu
(giờ Việt Nam)
Tóm tắt AI
Đội ngũ kỹ thuật Netflix đã giải quyết vấn đề phân vùng dữ liệu quá tải trong Apache Cassandra bằng cách kết hợp kỹ thuật phân đoạn theo thời gian và phân tách động theo ID, giúp giảm độ trễ truy vấn từ hàng giây xuống còn vài chục mili giây.
Bản dịch AI

Đội ngũ kỹ thuật của Netflix đã công bố một phương pháp xử lý các phân vùng lớn (wide partitions) trong Apache Cassandra. Công trình nghiên cứu này nhắm vào TimeSeries Abstraction của Netflix, một nền tảng dành cho dữ liệu sự kiện theo thời gian.
Tóm tắt (TL;DR)
Phân vùng lại động (Dynamic Repartitioning) là gì?
TimeSeries Abstraction của Netflix tiếp nhận và truy vấn hàng petabyte dữ liệu sự kiện theo thời gian với độ trễ tính bằng mili giây. Nền tảng này sử dụng Apache Cassandra 4.x làm lớp lưu trữ cơ sở. Cassandra được lựa chọn nhờ vào thông lượng, độ trễ, chi phí và sự ổn định trong vận hành.
Dữ liệu chuỗi thời gian được tổ chức thành các phân vùng, nhóm các sự kiện theo định danh và phạm vi thời gian. Các phân vùng này có thể trở nên "lớn" (wide) khi dữ liệu sự kiện tích tụ theo thời gian. Phân vùng lại động sẽ chia nhỏ một phân vùng quá khổ thành các phân vùng con một cách bất đồng bộ. Các ứng dụng vẫn tiếp tục truy vấn cùng một phân vùng logic trong khi cấu trúc lưu trữ được thay đổi một cách minh bạch.
Tại sao các phân vùng lớn lại gây ảnh hưởng đến hiệu suất đọc?
Đối với hầu hết các tập dữ liệu, độ trễ đọc trung bình chỉ ở mức dưới 10 mili giây. Khi các phân vùng trở nên quá lớn, độ trễ đọc ở phân vị cao (tail latency) có thể tăng lên đến hàng giây. Những lần đọc chậm này có thể gây ra lỗi quá thời gian (read timeouts). Trong các trường hợp nghiêm trọng, cụm máy chủ (cluster) sẽ gặp tình trạng tạm dừng do Garbage Collection, mức sử dụng CPU cao và nghẽn hàng đợi luồng (thread queueing).
Các máy chủ TimeSeries cũng xử lý thông lượng đọc rất cao, điều này làm trầm trọng thêm vấn đề. Việc mở rộng quy mô cụm Cassandra luôn là một lựa chọn, nhưng đội ngũ Netflix muốn có những giải pháp thông minh hơn thay vì chỉ đổ thêm tiền vào vấn đề.
Chiến lược phân vùng đằng sau TimeSeries
TimeSeries chia các tập dữ liệu thành các khối thời gian rời rạc: Time Slices (lát cắt thời gian), time buckets (nhóm thời gian) và event buckets (nhóm sự kiện). Điều này cho phép đội ngũ Netflix truy vấn và loại bỏ dữ liệu theo thời gian mà không cần tạo ra các tombstone. Khi một namespace (tập dữ liệu) được tạo, người dùng sẽ chỉ định các đặc điểm khối lượng công việc dự kiến. Một quy trình cung cấp hạ tầng (provisioning pipeline) sẽ chạy các mô phỏng Monte Carlo để chọn cấu hình hạ tầng và phân vùng phù hợp.
Cách tiếp cận chủ động này tỏ ra thiếu hiệu quả trong ba tình huống. Khối lượng công việc có thể chưa được biết hoặc ước tính không chính xác khi dự án mới bắt đầu. Khối lượng công việc cũng có thể thay đổi khi lưu lượng truy cập và yêu cầu sản phẩm thay đổi. Cuối cùng, các dữ liệu ngoại lai (outliers) luôn tồn tại, nơi một vài ID nhận được lượng sự kiện lớn hơn nhiều so với các ID khác.
Các Time Slice rời rạc cung cấp một lối thoát tự nhiên cho hai trường hợp đầu tiên. Mỗi Time Slice mới có thể sử dụng một chiến lược phân vùng khác nhau. Tuy nhiên, việc tinh chỉnh thủ công hàng ngàn tập dữ liệu là không bền vững, vì vậy cần phải có sự tự động hóa.
Giải pháp 1: Phân vùng lại theo Time Slice
Cassandra cung cấp các API nội quan như `nodetool tablehistograms` để xem các phân vị kích thước phân vùng. Một tiến trình chạy ngầm sẽ giám sát các biểu đồ này và hiển thị chúng thông qua một bảng ảo (virtual table) của Cassandra. Nó tính toán hệ số điều chỉnh khi kích thước phân vùng không đạt mật độ cấu hình. Mật độ đó thường được đặt trong khoảng từ 2 MiB đến 10 MiB, tùy thuộc vào khối lượng công việc.
Ví dụ, quá trình cung cấp hạ tầng từng chọn các time bucket 60 giây, tạo ra các phân vùng dưới 10 KB. Việc phân vùng quá mức đó gây ra hiện tượng khuếch đại đọc (read amplification) và nghẽn hàng đợi luồng. Tiến trình chạy ngầm sẽ cập nhật các Time Slice tương lai với một chiến lược đã được điều chỉnh:
Điều này giúp giảm độ trễ đọc và các lỗi quá thời gian do nghẽn hàng đợi luồng. Tuy nhiên, nó chỉ hữu ích khi phần lớn bảng cần được phân vùng lại. Nó không giúp ích khi chỉ một tỷ lệ nhỏ các ID bị "lớn". Đối với các trường hợp cục bộ đó, đội ngũ Netflix cung cấp ba tùy chọn:
Block IDs được áp dụng thông qua cấu hình, liệt kê các TimeSeries ID gây lỗi:
Các tùy chọn này không giúp ích khi các ID hợp lệ và quan trọng trở nên quá lớn. Những người gọi (callers) đó vẫn cần mọi sự kiện, và đó là lúc Giải pháp 2 được áp dụng.
Giải pháp 2: Phân vùng động theo ID
Phân vùng động là một quy trình bất đồng bộ giúp chia nhỏ các phân vùng lớn theo từng TimeSeries ID. Nó hoạt động ở cấp độ ID, không phải cấp độ bảng. Quy trình này có ba giai đoạn: Phát hiện (Detection), Lập kế hoạch & Chia nhỏ (Planning & Splitting), và Phục vụ đọc (Serving Reads).
Việc phát hiện diễn ra trên đường dẫn đọc (read path). Mỗi lần đọc sẽ theo dõi số byte đã đọc cho một phân vùng. Khi số byte vượt quá ngưỡng cấu hình, máy chủ sẽ phát ra một sự kiện tới Kafka:
Ở đây, `immutable` đánh dấu một phân vùng không còn nhận dữ liệu ghi. Trường `version` được dành riêng cho việc sử dụng trong tương lai, chẳng hạn như vô hiệu hóa. Đội ngũ Netflix thực hiện phát hiện khi đọc thay vì khi ghi, vì hầu hết dữ liệu không bao giờ cần phải chia nhỏ. Một số lần đọc trên các phân vùng lớn vẫn sẽ chậm trong vài giây cho đến khi quy trình bắt kịp. Việc triển khai ban đầu nhắm vào các phân vùng bất biến (immutable) để giảm độ phức tạp.
Giai đoạn lập kế hoạch sẽ đọc toàn bộ phân vùng một lần để tính toán một kế hoạch chia nhỏ chính xác. Tính năng kiểm tra (checkpointing) cho phép các lần đọc lập kế hoạch bị lỗi có thể tiếp tục từ điểm đã lưu cuối cùng. Bảng siêu dữ liệu `wide_row` lưu trữ trạng thái chia nhỏ, các checkpoint và thông tin định tuyến.
Giai đoạn chia nhỏ ủy quyền cho một chiến lược như `EventBucketPartitionSplitStrategy`. Nó gán nhiều event bucket hơn vào cùng một time bucket. Đối với các phân vùng cực lớn, nó giới hạn số lượng event bucket để kiểm soát hiện tượng khuếch đại đọc. Việc phân tán trên các phân vùng vẫn giúp phân phối các lần đọc trên các bản sao (replicas) của Cassandra.
Giai đoạn xác thực so sánh mã kiểm tra (checksum) trước khi chia nhỏ với mã kiểm tra sau khi chia nhỏ. Một lần chia nhỏ chỉ được đánh dấu là COMPLETED khi cả hai mã kiểm tra khớp nhau. Netflix cũng theo dõi kích thước phân vùng trước và sau khi chia để xác nhận các phân vùng mới có kích thước phù hợp.
Các máy chủ TimeSeries định kỳ tải các khóa phân vùng đã chia xong vào các bộ lọc Bloom trong bộ nhớ. Mỗi lần đọc đều kiểm tra bộ lọc Bloom, vốn phản hồi trong vài micro giây. Việc kiểm tra đó đủ nhỏ để gần như không gây ảnh hưởng đến người gọi. Khi có kết quả khớp, máy chủ sẽ đọc siêu dữ liệu `wide_row` để định tuyến truy vấn:
Việc đọc siêu dữ liệu đó được hỗ trợ bởi một bộ nhớ đệm đọc (read-through cache). `PartitionReader` hiện tại sau đó sẽ phục vụ các lần đọc từ các phân vùng con nhỏ hơn. Việc tái sử dụng cùng một lược đồ (schema) giúp giảm thiểu thay đổi mã nguồn, và các kết quả được hợp nhất trước khi trả về. Phân vùng lớn ban đầu không bao giờ bị xóa, cung cấp một phương án dự phòng an toàn cho các lỗi cục bộ và đảm bảo tính nhất quán cuối cùng.
Đội ngũ Netflix cũng xác minh các lần chia nhỏ ngoại tuyến bằng cách sử dụng các công việc Spark của Data Bridge. Việc triển khai theo từng giai đoạn đã tiến triển qua các chế độ đọc khi mức độ tin cậy tăng lên. Giai đoạn so sánh (Comparison) đã đối chiếu số byte được phục vụ bởi đường dẫn đọc cũ và mới ở chế độ shadow.
So sánh Giải pháp 1 và Giải pháp 2
Các trường hợp sử dụng kèm ví dụ
Trải nghiệm: Bản demo tương tác về bộ chia nhỏ (Splitter)
Trình mô phỏng được nhúng bên dưới mô hình hóa đường dẫn đọc từ đầu đến cuối. Hãy đặt kích thước phân vùng, ngưỡng phát hiện và số lượng event bucket mục tiêu. Việc chạy một lần đọc sẽ đi qua các bước Phát hiện, Lập kế hoạch, Chia nhỏ, Xác thực và Phục vụ. Nó hiển thị sự kiện phát hiện của Kafka và mức giảm độ trễ được mô hình hóa; các con số chỉ mang tính minh họa, không phải đo lường thực tế.
Kết quả
Độ trễ đọc trung bình cho các phân vùng lớn đã giảm từ hàng giây xuống còn vài chục mili giây. Độ trễ ở phân vị cao giảm từ vài giây xuống còn khoảng 200 ms hoặc tốt hơn. Các lỗi quá thời gian đọc giảm đáng kể, và đội ngũ Netflix báo cáo mức sử dụng CPU thấp hơn cùng với tình trạng nghẽn hàng đợi luồng ở mức tối thiểu. Nhìn chung, các cụm Cassandra đã trở nên ổn định hơn.
Đối với các hàng cực lớn, dịch vụ có thể phân trang và truy vấn các phân vùng trên 500MB trong khi vẫn duy trì tính khả dụng. Các phân vùng như vậy trước đây thường gây ra lỗi quá thời gian liên tục và các sự cố mất khả dụng. Một truy vấn phân trang sẽ trả về kết quả thành công, đánh đổi độ trễ tăng nhẹ để lấy tính khả dụng:
Đội ngũ Netflix liệt kê các công việc trong tương lai, bao gồm việc chia nhỏ các phân vùng lớn có thể thay đổi (mutable) và xử lý lại các lần chia nhỏ bị lỗi trước đó. Hai bài học nổi bật: Giảm phạm vi thay đổi của một tính năng phức tạp và triển khai theo từng bước mang lại hiệu quả vận hành cao. Việc đầu tư vào các cơ chế xây dựng niềm tin là hoàn toàn xứng đáng với độ phức tạp, phạm vi ảnh hưởng và tác động của tính năng này.
Xem chi tiết kỹ thuật tại đây. Ngoài ra, hãy thoải mái theo dõi chúng tôi trên Twitter và đừng quên tham gia SubReddit 150k+ ML của chúng tôi và đăng ký nhận Bản tin (Newsletter). Khoan đã! Bạn có dùng Telegram không? Bây giờ bạn cũng có thể tham gia cùng chúng tôi trên Telegram.
Bạn cần hợp tác với chúng tôi để quảng bá GitHub Repo, Trang Hugging Face, Phát hành sản phẩm hoặc Hội thảo trực tuyến, v.v.? Hãy kết nối với chúng tôi.
Bài viết được AI dịch và tổng hợp tự động từ MarkTechPost. Liên kết bài gốc ở phía trên. AIHOT.vn luôn dẫn nguồn đầy đủ — nếu bạn thấy điểm cần chỉnh sửa, hãy gửi ý kiến tại trang phản hồi.