Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Tôi khuyên bạn nên đọc bản ghi của báo cáo cuối năm 2019 của Alexander Valyalkin “Tối ưu hóa hoạt động trong VictoriaMetrics”

VictoriaSố liệu - một DBMS nhanh và có thể mở rộng để lưu trữ và xử lý dữ liệu dưới dạng chuỗi thời gian (ví dụ: bản ghi tạo thành thời gian và một tập hợp các giá trị tương ứng với thời gian này, có được thông qua việc thăm dò định kỳ về trạng thái của cảm biến hoặc thu thập dữ liệu số liệu).

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Đây là đường dẫn tới video của báo cáo này - https://youtu.be/MZ5P21j_HLE

Trang trình bày

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Nói cho chúng tôi biết về bạn. Tôi là Alexander Valyalkin. Đây tài khoản GitHub của tôi. Tôi đam mê Go và tối ưu hóa hiệu suất. Tôi đã viết rất nhiều thư viện hữu ích và không hữu ích. Họ bắt đầu với một trong hai fast, Hoặc với quick tiếp đầu ngữ.

Tôi hiện đang làm việc trên VictoriaMetrics. Nó là gì và tôi đang làm gì ở đó? Tôi sẽ nói về điều này trong bài trình bày này.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Bố cục của báo cáo như sau:

  • Đầu tiên, tôi sẽ cho bạn biết VictoriaMetrics là gì.
  • Sau đó tôi sẽ cho bạn biết chuỗi thời gian là gì.
  • Sau đó tôi sẽ cho bạn biết cơ sở dữ liệu chuỗi thời gian hoạt động như thế nào.
  • Tiếp theo, tôi sẽ cho bạn biết về kiến ​​trúc cơ sở dữ liệu: nó bao gồm những gì.
  • Sau đó, hãy chuyển sang những cách tối ưu hóa mà VictoriaMetrics có. Đây là sự tối ưu hóa cho chỉ mục đảo ngược và tối ưu hóa cho việc triển khai bitset trong Go.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Có ai trong số khán giả biết VictoriaMetrics là gì không? Wow, nhiều người đã biết rồi. Đó là một tin tốt. Dành cho những ai chưa biết thì đây là cơ sở dữ liệu chuỗi thời gian. Nó dựa trên kiến ​​trúc ClickHouse, dựa trên một số chi tiết về việc triển khai ClickHouse. Ví dụ: trên: MergeTree, tính toán song song trên tất cả các lõi bộ xử lý có sẵn và tối ưu hóa hiệu suất bằng cách làm việc trên các khối dữ liệu được đặt trong bộ đệm của bộ xử lý.

VictoriaMetrics cung cấp khả năng nén dữ liệu tốt hơn các cơ sở dữ liệu chuỗi thời gian khác.

Nó chia tỷ lệ theo chiều dọc - nghĩa là bạn có thể thêm nhiều bộ xử lý hơn, nhiều RAM hơn trên một máy tính. VictoriaMetrics sẽ sử dụng thành công các nguồn lực sẵn có này và sẽ cải thiện năng suất tuyến tính.

VictoriaMetrics cũng mở rộng theo chiều ngang - nghĩa là bạn có thể thêm các nút bổ sung vào cụm VictoriaMetrics và hiệu suất của nó sẽ tăng gần như tuyến tính.

Như bạn đã đoán, VictoriaMetrics là một cơ sở dữ liệu nhanh vì tôi không thể viết dữ liệu khác. Và nó được viết bằng Go nên tôi sẽ nói về nó tại buổi gặp mặt này.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Ai biết chuỗi thời gian là gì? Anh ấy cũng biết rất nhiều người. Chuỗi thời gian là chuỗi các cặp (timestamp, значение), trong đó các cặp này được sắp xếp theo thời gian. Giá trị là số dấu phẩy động – float64.

Mỗi chuỗi thời gian được xác định duy nhất bằng một khóa. Chìa khóa này bao gồm những gì? Nó bao gồm một tập hợp các cặp khóa-giá trị không trống.

Đây là một ví dụ về chuỗi thời gian. Chìa khóa của loạt bài này là danh sách các cặp: __name__="cpu_usage" là tên của thước đo, instance="my-server" - đây là máy tính mà số liệu này được thu thập, datacenter="us-east" - đây là trung tâm dữ liệu nơi đặt máy tính này.

Chúng tôi đã kết thúc với tên chuỗi thời gian bao gồm ba cặp khóa-giá trị. Khóa này tương ứng với danh sách các cặp (timestamp, value). t1, t3, t3, ..., tN - đây là dấu thời gian, 10, 20, 12, ..., 15 - các giá trị tương ứng. Đây là mức sử dụng CPU tại một thời điểm nhất định cho một chuỗi nhất định.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Chuỗi thời gian có thể được sử dụng ở đâu? Có ai có ý kiến ​​gì không?

  • Trong DevOps, bạn có thể đo CPU, RAM, mạng, rps, số lỗi, v.v.
  • IoT - chúng ta có thể đo nhiệt độ, áp suất, tọa độ địa lý và những thứ khác.
  • Ngoài ra, về tài chính – chúng tôi có thể theo dõi giá của tất cả các loại cổ phiếu và tiền tệ.
  • Ngoài ra, chuỗi thời gian có thể được sử dụng để giám sát quá trình sản xuất tại các nhà máy. Chúng tôi có những người dùng sử dụng VictoriaMetrics để giám sát tua-bin gió cho robot.
  • Chuỗi thời gian cũng hữu ích cho việc thu thập thông tin từ cảm biến của các thiết bị khác nhau. Ví dụ, đối với động cơ; để đo áp suất lốp; để đo tốc độ, khoảng cách; để đo mức tiêu thụ xăng, v.v.
  • Chuỗi thời gian cũng có thể được sử dụng để giám sát máy bay. Mỗi máy bay có một hộp đen thu thập chuỗi thời gian về các thông số khác nhau về tình trạng của máy bay. Chuỗi thời gian cũng được sử dụng trong ngành hàng không vũ trụ.
  • Chăm sóc sức khỏe là huyết áp, mạch, v.v.

Có thể có nhiều ứng dụng khác mà tôi đã quên, nhưng tôi hy vọng bạn hiểu rằng chuỗi thời gian được sử dụng tích cực trong thế giới hiện đại. Và khối lượng sử dụng của họ đang tăng lên hàng năm.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Tại sao bạn cần một cơ sở dữ liệu chuỗi thời gian? Tại sao bạn không thể sử dụng cơ sở dữ liệu quan hệ thông thường để lưu trữ chuỗi thời gian?

Bởi vì chuỗi thời gian thường chứa một lượng thông tin lớn, khó lưu trữ và xử lý trong cơ sở dữ liệu thông thường. Vì vậy, cơ sở dữ liệu chuyên biệt về chuỗi thời gian đã xuất hiện. Những căn cứ này lưu trữ điểm một cách hiệu quả (timestamp, value) với khóa đã cho. Họ cung cấp API để đọc dữ liệu được lưu trữ theo khóa, theo một cặp khóa-giá trị hoặc theo nhiều cặp khóa-giá trị hoặc theo biểu thức chính quy. Ví dụ: bạn muốn tìm tải CPU của tất cả các dịch vụ của mình trong một trung tâm dữ liệu ở Mỹ thì bạn cần sử dụng truy vấn giả này.

Thông thường, cơ sở dữ liệu chuỗi thời gian cung cấp các ngôn ngữ truy vấn chuyên biệt vì SQL chuỗi thời gian không phù hợp lắm. Mặc dù có cơ sở dữ liệu hỗ trợ SQL nhưng nó không phù hợp lắm. Ngôn ngữ truy vấn như PromQL, dòngQL, Phun ra, Q. Tôi hy vọng rằng ai đó đã nghe ít nhất một trong những ngôn ngữ này. Có lẽ nhiều người đã nghe nói về PromQL. Đây là ngôn ngữ truy vấn Prometheus.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Đây là kiến ​​trúc cơ sở dữ liệu chuỗi thời gian hiện đại khi sử dụng VictoriaMetrics làm ví dụ.

Nó bao gồm hai phần. Đây là nơi lưu trữ chỉ mục đảo ngược và lưu trữ các giá trị chuỗi thời gian. Các kho lưu trữ này được tách ra.

Khi một bản ghi mới đến cơ sở dữ liệu, trước tiên chúng tôi truy cập vào chỉ mục đảo ngược để tìm mã định danh chuỗi thời gian cho một tập hợp nhất định label=value cho một số liệu nhất định. Chúng tôi tìm thấy mã định danh này và lưu giá trị vào kho dữ liệu.

Khi có yêu cầu lấy dữ liệu từ TSDB, trước tiên chúng ta đi đến chỉ mục đảo ngược. Hãy lấy mọi thứ timeseries_ids bản ghi phù hợp với bộ này label=value. Và sau đó chúng tôi nhận được tất cả dữ liệu cần thiết từ kho dữ liệu, được lập chỉ mục bởi timeseries_ids.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Hãy xem một ví dụ về cách cơ sở dữ liệu chuỗi thời gian xử lý một truy vấn chọn đến.

  • Trước hết cô ấy có được mọi thứ timeseries_ids từ một chỉ mục đảo ngược có chứa các cặp đã cho label=valuehoặc thỏa mãn một biểu thức chính quy nhất định.
  • Sau đó, nó lấy tất cả các điểm dữ liệu từ bộ lưu trữ dữ liệu tại một khoảng thời gian nhất định cho những điểm được tìm thấy timeseries_ids.
  • Sau đó, cơ sở dữ liệu thực hiện một số tính toán trên các điểm dữ liệu này theo yêu cầu của người dùng. Và sau đó nó trả về câu trả lời.

Trong phần trình bày này tôi sẽ kể cho bạn nghe về phần đầu tiên. Đây là một tìm kiếm timeseries_ids bằng chỉ số đảo ngược. Bạn có thể xem về phần thứ hai và phần thứ ba sau Nguồn VictoriaMetrics, hoặc đợi đến khi tôi chuẩn bị các báo cáo khác :)

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Hãy chuyển sang chỉ số đảo ngược. Nhiều người có thể nghĩ điều này thật đơn giản. Ai biết chỉ số đảo ngược là gì và nó hoạt động như thế nào? Ồ, không còn nhiều người nữa. Chúng ta hãy cố gắng hiểu nó là gì.

Nó thực sự đơn giản. Nó chỉ đơn giản là một từ điển ánh xạ khóa tới một giá trị. Chìa khóa là gì? Cặp đôi này label=valueĐâu label и value - đây là những dòng. Và các giá trị là một tập hợp timeseries_ids, bao gồm cặp đã cho label=value.

Chỉ mục đảo ngược cho phép bạn nhanh chóng tìm thấy mọi thứ timeseries_ids, đã cho label=value.

Nó cũng cho phép bạn nhanh chóng tìm thấy timeseries_ids chuỗi thời gian cho một số cặp label=valuehoặc dành cho các cặp đôi label=regexp. Làm thế nào điều này xảy ra? Bằng cách tìm giao điểm của tập hợp timeseries_ids cho mỗi cặp label=value.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Chúng ta hãy xem xét các cách triển khai khác nhau của chỉ mục đảo ngược. Hãy bắt đầu với cách thực hiện ngây thơ đơn giản nhất. Cô ấy trông như thế này.

Chức năng getMetricIDs nhận được một danh sách các chuỗi. Mỗi dòng chứa label=value. Hàm này trả về một danh sách metricIDs.

Làm thế nào nó hoạt động? Ở đây chúng ta có một biến toàn cục được gọi là invertedIndex. Đây là một từ điển thông thường (map), sẽ ánh xạ chuỗi thành lát int. Dòng chứa label=value.

Thực hiện chức năng: nhận metricIDs lần đầu tiên label=value, sau đó chúng ta sẽ xem xét mọi thứ khác label=value, chúng tôi hiểu rồi metricIDs cho họ. Và gọi hàm intersectInts, sẽ được thảo luận dưới đây. Và hàm này trả về giao điểm của các danh sách này.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Như bạn có thể thấy, việc triển khai chỉ mục đảo ngược không phức tạp lắm. Nhưng đây là một thực hiện ngây thơ. Nó có nhược điểm gì? Nhược điểm chính của việc triển khai đơn giản là chỉ mục đảo ngược như vậy được lưu trữ trong RAM. Sau khi khởi động lại ứng dụng chúng ta sẽ mất chỉ mục này. Không có lưu chỉ mục này vào đĩa. Chỉ mục đảo ngược như vậy dường như không phù hợp với cơ sở dữ liệu.

Hạn chế thứ hai cũng liên quan đến bộ nhớ. Chỉ số đảo ngược phải phù hợp với RAM. Nếu nó vượt quá kích thước của RAM thì rõ ràng chúng ta sẽ gặp lỗi out of bộ nhớ. Và chương trình sẽ không hoạt động.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Vấn đề này có thể được giải quyết bằng cách sử dụng các giải pháp có sẵn như Cấp độDBHoặc ĐáDB.

Nói tóm lại, chúng ta cần một cơ sở dữ liệu cho phép chúng ta thực hiện ba thao tác một cách nhanh chóng.

  • Hoạt động đầu tiên là ghi ключ-значение tới cơ sở dữ liệu này. Cô ấy làm việc này rất nhanh, ở đâu ключ-значение là các chuỗi tùy ý.
  • Thao tác thứ hai là tìm kiếm nhanh một giá trị bằng cách sử dụng một khóa nhất định.
  • Và thao tác thứ ba là tìm kiếm nhanh tất cả các giá trị theo một tiền tố nhất định.

LevelDB và RocksDB - những cơ sở dữ liệu này được phát triển bởi Google và Facebook. Đầu tiên là LevelDB. Sau đó, những người ở Facebook đã lấy LevelDB và bắt đầu cải thiện nó, họ đã tạo ra RocksDB. Giờ đây, hầu hết tất cả cơ sở dữ liệu nội bộ đều hoạt động trên RocksDB bên trong Facebook, bao gồm cả những cơ sở dữ liệu đã được chuyển sang RocksDB và MySQL. Họ đặt tên cho anh ấy MyRocks.

Một chỉ mục đảo ngược có thể được triển khai bằng LevelDB. Làm thế nào để làm nó? Chúng tôi lưu làm chìa khóa label=value. Và giá trị là mã định danh của chuỗi thời gian nơi cặp này hiện diện label=value.

Nếu chúng ta có nhiều chuỗi thời gian với một cặp nhất định label=value, khi đó sẽ có nhiều hàng trong cơ sở dữ liệu này có cùng khóa và khác nhau timeseries_ids. Để có được danh sách tất cả timeseries_ids, bắt đầu bằng cái này label=prefix, chúng tôi thực hiện quét phạm vi mà cơ sở dữ liệu này được tối ưu hóa. Nghĩa là, chúng tôi chọn tất cả các dòng bắt đầu bằng label=prefix và nhận được những thứ cần thiết timeseries_ids.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Đây là cách triển khai mẫu về giao diện của nó trong Go. Chúng tôi có một chỉ số đảo ngược. Đây là LevelDB.

Chức năng này giống như cách thực hiện đơn giản. Nó lặp lại việc thực hiện ngây thơ gần như từng dòng một. Điểm duy nhất là thay vì chuyển sang map chúng tôi truy cập vào chỉ mục đảo ngược. Chúng tôi nhận được tất cả các giá trị đầu tiên label=value. Sau đó chúng ta duyệt qua tất cả các cặp còn lại label=value và nhận các bộ ID số liệu tương ứng cho chúng. Sau đó chúng ta tìm thấy giao lộ.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Mọi thứ có vẻ ổn, nhưng giải pháp này có những hạn chế. VictoriaMetrics ban đầu triển khai chỉ mục đảo ngược dựa trên LevelDB. Nhưng cuối cùng tôi phải từ bỏ nó.

Tại sao? Bởi vì LevelDB chậm hơn so với cách triển khai đơn giản. Trong một triển khai đơn giản, với một khóa nhất định, chúng ta ngay lập tức truy xuất toàn bộ lát cắt metricIDs. Đây là thao tác rất nhanh - toàn bộ lát cắt đã sẵn sàng để sử dụng.

Trong LevelDB, mỗi khi một hàm được gọi GetValues bạn cần phải đọc qua tất cả các dòng bắt đầu bằng label=value. Và nhận giá trị cho mỗi dòng timeseries_ids. Như vậy timeseries_ids thu thập một lát trong số này timeseries_ids. Rõ ràng, việc này chậm hơn nhiều so với việc chỉ truy cập bản đồ thông thường bằng phím.

Hạn chế thứ hai là LevelDB được viết bằng C. Việc gọi các hàm C từ Go không nhanh lắm. Phải mất hàng trăm nano giây. Tốc độ này không nhanh lắm, vì so với lệnh gọi hàm thông thường được viết bằng go, mất 1-5 nano giây, thì sự khác biệt về hiệu suất là hàng chục lần. Đối với VictoriaMetrics đây là một sai sót chết người :)

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Vì vậy, tôi đã viết phần triển khai chỉ mục đảo ngược của riêng mình. Và anh đã gọi cô ấy tập hợp hợp nhất.

Mergeset dựa trên cấu trúc dữ liệu MergeTree. Cấu trúc dữ liệu này được mượn từ ClickHouse. Rõ ràng, mergeset cần được tối ưu hóa để tìm kiếm nhanh timeseries_ids theo key đã cho. Mergeset được viết hoàn toàn bằng Go. Bạn có thể thấy Nguồn VictoriaMetrics trên GitHub. Việc thực hiện mergeset nằm trong thư mục /lib/hợp nhất. Bạn có thể cố gắng tìm hiểu những gì đang xảy ra ở đó.

API hợp nhất rất giống với LevelDB và RocksDB. Nghĩa là, nó cho phép bạn nhanh chóng lưu các bản ghi mới vào đó và nhanh chóng chọn các bản ghi theo tiền tố nhất định.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Chúng ta sẽ nói về nhược điểm của mergeset sau. Bây giờ hãy nói về những vấn đề nảy sinh với VictoriaMetrics trong quá trình sản xuất khi triển khai chỉ mục đảo ngược.

Tại sao họ phát sinh?

Lý do đầu tiên là tỷ lệ rời bỏ cao. Được dịch sang tiếng Nga, đây là sự thay đổi thường xuyên trong chuỗi thời gian. Đây là lúc một chuỗi thời gian kết thúc và một chuỗi thời gian mới bắt đầu hoặc nhiều chuỗi thời gian mới bắt đầu. Và điều này xảy ra thường xuyên.

Lý do thứ hai là số lượng lớn chuỗi thời gian. Ban đầu, khi việc giám sát trở nên phổ biến thì số lượng chuỗi thời gian còn ít. Ví dụ: đối với mỗi máy tính, bạn cần theo dõi tải CPU, bộ nhớ, mạng và ổ đĩa. 4 chuỗi thời gian trên mỗi máy tính. Giả sử bạn có 100 máy tính và 400 chuỗi thời gian. Đây là rất ít.

Theo thời gian, mọi người nhận ra rằng họ có thể đo lường được thông tin chi tiết hơn. Ví dụ: đo tải không phải của toàn bộ bộ xử lý mà riêng biệt từng lõi bộ xử lý. Nếu bạn có 40 lõi bộ xử lý thì bạn có chuỗi thời gian gấp 40 lần để đo tải bộ xử lý.

Nhưng đó không phải là tất cả. Mỗi lõi bộ xử lý có thể có một số trạng thái, chẳng hạn như không hoạt động, khi nó không hoạt động. Và cũng có thể hoạt động trong không gian người dùng, hoạt động trong không gian kernel và các trạng thái khác. Và mỗi trạng thái như vậy cũng có thể được đo lường dưới dạng một chuỗi thời gian riêng biệt. Điều này cũng làm tăng số lượng hàng lên 7-8 lần.

Từ một số liệu, chúng tôi nhận được 40 x 8 = 320 số liệu cho chỉ một máy tính. Nhân với 100, chúng ta được 32 thay vì 000.

Sau đó Kubernetes xuất hiện. Và mọi chuyện còn tệ hơn vì Kubernetes có thể lưu trữ nhiều dịch vụ khác nhau. Mỗi dịch vụ trong Kubernetes bao gồm nhiều nhóm. Và tất cả điều này cần phải được theo dõi. Ngoài ra, chúng tôi liên tục triển khai các phiên bản mới của dịch vụ của bạn. Đối với mỗi phiên bản mới, chuỗi thời gian mới phải được tạo. Kết quả là, số lượng chuỗi thời gian tăng theo cấp số nhân và chúng ta phải đối mặt với vấn đề về số lượng chuỗi thời gian lớn, được gọi là số lượng số cao. VictoriaMetrics đã giải quyết vấn đề này thành công so với các cơ sở dữ liệu chuỗi thời gian khác.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Chúng ta hãy xem xét kỹ hơn về tỷ lệ rời bỏ cao. Điều gì gây ra tỷ lệ rời bỏ cao trong sản xuất? Bởi vì một số ý nghĩa của nhãn và thẻ luôn thay đổi.

Ví dụ: lấy Kubernetes, có khái niệm deployment, tức là khi phiên bản mới của ứng dụng của bạn được triển khai. Vì lý do nào đó, các nhà phát triển Kubernetes đã quyết định thêm id triển khai vào nhãn.

Điều này đã dẫn tới điều gì? Hơn nữa, với mỗi lần triển khai mới, tất cả chuỗi thời gian cũ đều bị gián đoạn và thay vào đó là chuỗi thời gian mới bắt đầu bằng giá trị nhãn mới deployment_id. Có thể có hàng trăm ngàn, thậm chí hàng triệu hàng như vậy.

Điều quan trọng của tất cả những điều này là tổng số chuỗi thời gian tăng lên nhưng số lượng chuỗi thời gian hiện đang hoạt động và nhận dữ liệu vẫn không đổi. Trạng thái này được gọi là tỷ lệ rời bỏ cao.

Vấn đề chính của tỷ lệ rời bỏ cao là đảm bảo tốc độ tìm kiếm không đổi trong mọi chuỗi thời gian đối với một bộ nhãn nhất định trong một khoảng thời gian nhất định. Thông thường đây là khoảng thời gian cho giờ cuối cùng hoặc ngày cuối cùng.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Làm thế nào để giải quyết vấn đề này? Đây là lựa chọn đầu tiên. Điều này nhằm chia chỉ số đảo ngược thành các phần độc lập theo thời gian. Tức là, một khoảng thời gian trôi qua, chúng ta hoàn thành công việc với chỉ mục đảo ngược hiện tại. Và tạo một chỉ mục đảo ngược mới. Một khoảng thời gian nữa trôi qua, chúng ta tạo ra cái khác và cái khác.

Và khi lấy mẫu từ các chỉ số đảo ngược này, chúng tôi tìm thấy một tập hợp các chỉ số đảo ngược nằm trong khoảng nhất định. Và theo đó, chúng tôi chọn id của chuỗi thời gian từ đó.

Điều này giúp tiết kiệm tài nguyên vì chúng ta không phải xem xét các phần không nằm trong khoảng thời gian nhất định. Thông thường, nếu chúng tôi chọn dữ liệu cho giờ trước thì trong các khoảng thời gian trước đó, chúng tôi sẽ bỏ qua các yêu cầu.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Có một lựa chọn khác để giải quyết vấn đề này. Điều này nhằm lưu trữ cho mỗi ngày một danh sách id riêng biệt của chuỗi thời gian xảy ra vào ngày đó.

Ưu điểm của giải pháp này so với giải pháp trước đó là chúng tôi không trùng lặp thông tin chuỗi thời gian không biến mất theo thời gian. Chúng liên tục hiện diện và không thay đổi.

Nhược điểm là giải pháp như vậy khó thực hiện hơn và khó gỡ lỗi hơn. Và VictoriaMetrics đã chọn giải pháp này. Đây là cách nó đã xảy ra trong lịch sử. Giải pháp này cũng hoạt động tốt so với giải pháp trước đó. Bởi vì giải pháp này không được triển khai do cần phải sao chép dữ liệu trong từng phân vùng để chuỗi thời gian không thay đổi, tức là không biến mất theo thời gian. VictoriaMetrics chủ yếu được tối ưu hóa cho việc tiêu thụ dung lượng ổ đĩa và việc triển khai trước đó đã khiến mức tiêu thụ dung lượng ổ đĩa trở nên tồi tệ hơn. Nhưng cách triển khai này phù hợp hơn để giảm thiểu mức tiêu thụ dung lượng ổ đĩa nên nó đã được chọn.

Tôi phải chiến đấu với cô ấy. Điều khó khăn là trong quá trình triển khai này, bạn vẫn cần chọn một số lượng lớn hơn nhiều timeseries_ids cho dữ liệu hơn là khi chỉ mục đảo ngược được phân vùng theo thời gian.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Chúng tôi đã giải quyết vấn đề này như thế nào? Chúng tôi đã giải quyết vấn đề này theo cách nguyên bản - bằng cách lưu trữ một số mã định danh chuỗi thời gian trong mỗi mục nhập chỉ mục đảo ngược thay vì một mã định danh. Tức là chúng ta có chìa khóa label=value, xảy ra ở mọi chuỗi thời gian. Và bây giờ chúng tôi lưu một số timeseries_ids trong một mục.

Đây là một ví dụ. Trước đây chúng tôi có N mục, nhưng bây giờ chúng tôi có một mục có tiền tố giống với tất cả các mục khác. Đối với mục nhập trước, giá trị chứa tất cả các id chuỗi thời gian.

Điều này giúp tăng tốc độ quét của một chỉ mục đảo ngược như vậy lên tới 10 lần. Và nó cho phép chúng tôi giảm mức tiêu thụ bộ nhớ cho bộ đệm, vì bây giờ chúng tôi lưu trữ chuỗi label=value chỉ một lần trong bộ đệm N lần. Và dòng này có thể lớn nếu bạn lưu trữ các dòng dài trong thẻ và nhãn của mình, điều mà Kubernetes thích nhét vào đó.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Một tùy chọn khác để tăng tốc độ tìm kiếm trên chỉ mục đảo ngược là sharding. Tạo một số chỉ mục đảo ngược thay vì một và phân chia dữ liệu giữa chúng bằng khóa. Đây là một bộ key=value hơi nước. Nghĩa là, chúng tôi nhận được một số chỉ mục đảo ngược độc lập mà chúng tôi có thể truy vấn song song trên một số bộ xử lý. Các triển khai trước đây chỉ cho phép hoạt động ở chế độ bộ xử lý đơn, tức là chỉ quét dữ liệu trên một lõi. Giải pháp này cho phép bạn quét dữ liệu trên nhiều lõi cùng một lúc, như ClickHouse thích làm. Đây là những gì chúng tôi dự định thực hiện.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Bây giờ chúng ta hãy quay trở lại với con cừu của chúng ta - với chức năng giao nhau timeseries_ids. Hãy xem xét những triển khai nào có thể có. Chức năng này cho phép bạn tìm timeseries_ids cho một bộ nhất định label=value.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Tùy chọn đầu tiên là một triển khai đơn giản. Hai vòng lặp lồng nhau. Ở đây chúng ta nhận được đầu vào chức năng intersectInts Hai lát - a и b. Ở đầu ra, nó sẽ trả về cho chúng ta giao điểm của các lát cắt này.

Việc triển khai ngây thơ trông như thế này. Chúng tôi lặp lại tất cả các giá trị từ slice a, bên trong vòng lặp này chúng ta duyệt qua tất cả các giá trị của slice b. Và chúng tôi so sánh chúng. Nếu chúng khớp nhau thì chúng ta đã tìm được giao điểm. Và lưu nó vào result.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Những bất lợi là gì? Độ phức tạp bậc hai là nhược điểm chính của nó. Ví dụ: nếu kích thước của bạn là lát a и b một triệu lần, thì chức năng này sẽ không bao giờ trả lời cho bạn. Bởi vì nó sẽ cần thực hiện một nghìn tỷ lần lặp, con số này là rất nhiều ngay cả đối với các máy tính hiện đại.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Việc thực hiện thứ hai dựa trên bản đồ. Chúng tôi tạo ra bản đồ. Chúng tôi đặt tất cả các giá trị từ slice vào bản đồ này a. Sau đó chúng ta đi qua lát cắt trong một vòng lặp riêng biệt b. Và chúng tôi kiểm tra xem giá trị này có phải từ lát cắt hay không b trong bản đồ. Nếu nó tồn tại thì thêm nó vào kết quả.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Những lợi ích là gì? Ưu điểm là chỉ có độ phức tạp tuyến tính. Nghĩa là, hàm sẽ thực thi nhanh hơn nhiều đối với các lát lớn hơn. Đối với một lát cắt có kích thước một triệu, hàm này sẽ thực thi trong 2 triệu lần lặp, trái ngược với hàng nghìn tỷ lần lặp của hàm trước đó.

Nhược điểm là chức năng này cần nhiều bộ nhớ hơn để tạo bản đồ này.

Hạn chế thứ hai là chi phí băm lớn. Nhược điểm này không rõ ràng lắm. Và đối với chúng tôi, điều đó cũng không rõ ràng lắm, vì vậy lúc đầu ở VictoriaMetrics, việc thực hiện giao lộ được thực hiện thông qua bản đồ. Nhưng sau đó, việc lập hồ sơ cho thấy bộ xử lý chính dành thời gian ghi vào bản đồ và kiểm tra sự hiện diện của một giá trị trong bản đồ này.

Tại sao thời gian CPU bị lãng phí ở những nơi này? Bởi vì Go thực hiện thao tác băm trên những dòng này. Nghĩa là, nó tính toán hàm băm của khóa để sau đó truy cập vào nó tại một chỉ mục nhất định trong HashMap. Hoạt động tính toán băm được hoàn thành trong hàng chục nano giây. Quá trình này diễn ra chậm đối với VictoriaMetrics.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Tôi quyết định triển khai một bitset được tối ưu hóa riêng cho trường hợp này. Đây là giao điểm của hai lát cắt bây giờ trông như thế nào. Ở đây chúng tôi tạo ra một bitset. Chúng tôi thêm các phần tử từ lát cắt đầu tiên vào nó. Sau đó, chúng tôi kiểm tra sự hiện diện của các phần tử này trong lát cắt thứ hai. Và thêm chúng vào kết quả. Nghĩa là, nó gần như không khác gì ví dụ trước. Điều duy nhất ở đây là chúng tôi đã thay thế quyền truy cập vào bản đồ bằng các chức năng tùy chỉnh add и has.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Thoạt nhìn, có vẻ như nó sẽ hoạt động chậm hơn nếu trước đó bản đồ tiêu chuẩn được sử dụng ở đó và sau đó một số chức năng khác được gọi, nhưng việc lập hồ sơ cho thấy rằng thứ này hoạt động nhanh hơn 10 lần so với bản đồ tiêu chuẩn trong trường hợp của VictoriaMetrics.

Ngoài ra, nó sử dụng ít bộ nhớ hơn so với việc triển khai bản đồ. Bởi vì chúng ta đang lưu trữ các bit ở đây thay vì các giá trị XNUMX byte.

Nhược điểm của việc thực hiện này là nó không quá rõ ràng, không tầm thường.

Một nhược điểm khác mà nhiều người có thể không nhận thấy là việc triển khai này có thể không hoạt động tốt trong một số trường hợp. Nghĩa là, nó được tối ưu hóa cho một trường hợp cụ thể, cho trường hợp giao nhau của các id chuỗi thời gian VictoriaMetrics. Điều này không có nghĩa là nó phù hợp cho mọi trường hợp. Nếu sử dụng không đúng cách, chúng ta sẽ không tăng hiệu suất mà sẽ gặp lỗi hết bộ nhớ và hiệu suất chậm lại.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Hãy xem xét việc thực hiện cấu trúc này. Nếu bạn muốn xem, nó nằm trong nguồn VictoriaMetrics, trong thư mục lib/uint64set. Nó được tối ưu hóa đặc biệt cho trường hợp VictoriaMetrics, trong đó timeseries_id là giá trị 64 bit, trong đó 32 bit đầu tiên về cơ bản không đổi và chỉ 32 bit cuối cùng thay đổi.

Cấu trúc dữ liệu này không được lưu trữ trên đĩa, nó chỉ hoạt động trong bộ nhớ.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Đây là API của nó. Nó không phức tạp lắm. API được thiết kế riêng cho một ví dụ cụ thể về việc sử dụng VictoriaMetrics. Đó là, không có chức năng không cần thiết ở đây. Dưới đây là các chức năng được VictoriaMetrics sử dụng rõ ràng.

Có chức năng add, thêm các giá trị mới. Có một chức năng has, để kiểm tra các giá trị mới. Và có một chức năng del, loại bỏ các giá trị. Có chức năng trợ giúp len, trả về kích thước của tập hợp. Chức năng clone nhân bản rất nhiều. Và chức năng appendto chuyển đổi tập hợp này thành lát timeseries_ids.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Việc triển khai cấu trúc dữ liệu này trông như thế này. tập hợp có hai phần tử:

  • ItemsCount là trường trợ giúp để nhanh chóng trả về số phần tử trong một tập hợp. Có thể thực hiện mà không cần trường phụ trợ này, nhưng nó phải được thêm vào đây vì VictoriaMetrics thường truy vấn độ dài bitset trong thuật toán của nó.

  • Trường thứ hai là buckets. Đây là lát cắt từ cấu trúc bucket32. Mỗi cấu trúc lưu trữ hi cánh đồng. Đây là 32 bit trên. Và hai lát - b16his и buckets của bucket16 cấu trúc.

16 bit trên cùng của phần thứ hai của cấu trúc 64 bit được lưu trữ ở đây. Và ở đây các bit được lưu trữ cho 16 bit thấp hơn của mỗi byte.

Bucket64 bao gồm một mảng uint64. Độ dài được tính bằng cách sử dụng các hằng số này. Trong một bucket16 tối đa có thể được lưu trữ 2^16=65536 chút. Nếu bạn chia số này cho 8 thì nó là 8 kilobyte. Nếu chia cho 8 lần nữa thì là 1000 uint64 nghĩa. Đó là Bucket16 – đây là cấu trúc 8 kilobyte của chúng tôi.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Chúng ta hãy xem cách triển khai một trong các phương pháp của cấu trúc này để thêm giá trị mới.

Tất cả bắt đầu với uint64 ý nghĩa. Chúng tôi tính toán 32 bit trên, chúng tôi tính toán 32 bit dưới. Chúng ta hãy đi qua tất cả mọi thứ buckets. Chúng tôi so sánh 32 bit hàng đầu trong mỗi nhóm với giá trị được thêm vào. Và nếu chúng khớp nhau thì chúng ta gọi hàm add trong cấu trúc b32 buckets. Và thêm 32 bit thấp hơn vào đó. Và nếu nó quay trở lại true, thì điều này có nghĩa là chúng tôi đã thêm một giá trị như vậy vào đó và chúng tôi không có giá trị đó. Nếu nó trở lại false, thì ý nghĩa như vậy đã tồn tại. Sau đó chúng ta tăng số lượng phần tử trong cấu trúc.

Nếu chúng tôi chưa tìm thấy thứ bạn cần bucket với giá trị hi được yêu cầu thì chúng ta gọi hàm addAlloc, nó sẽ tạo ra một cái mới bucket, thêm nó vào cấu trúc xô.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Đây là việc thực hiện chức năng b32.add. Nó tương tự như việc thực hiện trước đó. Chúng tôi tính toán 16 bit có ý nghĩa nhất, 16 bit có ý nghĩa nhỏ nhất.

Sau đó chúng ta đi qua tất cả 16 bit trên. Chúng tôi tìm thấy các trận đấu. Và nếu có sự trùng khớp, chúng tôi gọi phương thức add mà chúng tôi sẽ xem xét ở trang tiếp theo cho bucket16.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Và đây là mức thấp nhất, cần được tối ưu hóa nhiều nhất có thể. Chúng tôi tính toán cho uint64 giá trị id theo bit lát và cả bitmask. Đây là mặt nạ cho một giá trị 64 bit nhất định, có thể được sử dụng để kiểm tra sự hiện diện của bit này hoặc đặt nó. Chúng tôi kiểm tra xem bit này có được đặt hay không và đặt nó cũng như trả về sự hiện diện. Đây là cách triển khai của chúng tôi, cho phép chúng tôi tăng tốc hoạt động giao nhau của các id của chuỗi thời gian lên 10 lần so với các bản đồ thông thường.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Ngoài sự tối ưu hóa này, VictoriaMetrics còn có nhiều sự tối ưu hóa khác. Hầu hết các tối ưu hóa này được thêm vào đều có lý do nhưng sau khi lập hồ sơ mã trong quá trình sản xuất.

Đây là quy tắc tối ưu hóa chính - không thêm tối ưu hóa với giả định rằng sẽ có nút cổ chai ở đây, vì có thể sẽ không có nút cổ chai ở đó. Tối ưu hóa thường làm giảm chất lượng của mã. Do đó, chỉ nên tối ưu hóa sau khi lập hồ sơ và tốt nhất là trong quá trình sản xuất để đây là dữ liệu thực. Nếu bất kỳ ai quan tâm, bạn có thể xem mã nguồn VictoriaMetrics và khám phá các cách tối ưu hóa khác có ở đó.

Tối ưu hóa hoạt động đi trong VictoriaMetrics. Alexander Valyalkin

Tôi có một câu hỏi về bitset. Rất giống với cách triển khai bool vectơ C++, bitset được tối ưu hóa. Bạn đã thực hiện từ đó?

Không, không phải từ đó. Khi triển khai bộ bit này, tôi đã được hướng dẫn bởi kiến ​​thức về cấu trúc của các chuỗi thời gian id này, được sử dụng trong VictoriaMetrics. Và cấu trúc của chúng sao cho 32 bit trên về cơ bản là không đổi. 32 bit thấp hơn có thể thay đổi. Bit càng thấp thì nó càng có thể thay đổi thường xuyên. Do đó, việc triển khai này được tối ưu hóa đặc biệt cho cấu trúc dữ liệu này. Theo như tôi biết, việc triển khai C++ được tối ưu hóa cho trường hợp chung. Nếu bạn tối ưu cho trường hợp tổng quát thì có nghĩa là nó sẽ không tối ưu nhất cho một trường hợp cụ thể.

Tôi cũng khuyên bạn nên xem báo cáo của Alexey Milovid. Khoảng một tháng trước, anh ấy đã nói về việc tối ưu hóa ClickHouse cho các chuyên môn cụ thể. Anh ấy chỉ nói rằng trong trường hợp chung, việc triển khai C++ hoặc một số cách triển khai khác được điều chỉnh để hoạt động tốt ở mức trung bình trong bệnh viện. Nó có thể hoạt động kém hơn so với cách triển khai dành riêng cho kiến ​​thức như của chúng tôi, nơi chúng tôi biết rằng 32 bit hàng đầu hầu như không đổi.

Tôi có câu hỏi thứ hai. Sự khác biệt cơ bản so với InfluxDB là gì?

Có nhiều khác biệt cơ bản. Về hiệu suất và mức tiêu thụ bộ nhớ, InfluxDB trong các thử nghiệm cho thấy mức tiêu thụ bộ nhớ cao hơn 10 lần đối với chuỗi thời gian có số lượng bản số cao, khi bạn có rất nhiều bộ nhớ, chẳng hạn như hàng triệu. Ví dụ: VictoriaMetrics tiêu thụ 1 GB trên một triệu hàng hiện hoạt, trong khi InfluxDB tiêu thụ 10 GB. Và đó là một sự khác biệt lớn.

Điểm khác biệt cơ bản thứ hai là InfluxDB có các ngôn ngữ truy vấn lạ - Flux và InfluxQL. Chúng không thuận tiện lắm khi làm việc với chuỗi thời gian so với PromQL, được hỗ trợ bởi VictoriaMetrics. PromQL là ngôn ngữ truy vấn của Prometheus.

Và một điểm khác biệt nữa là InfluxDB có mô hình dữ liệu hơi lạ, trong đó mỗi dòng có thể lưu trữ một số trường với một bộ thẻ khác nhau. Những dòng này được chia thành nhiều bảng khác nhau. Những sự phức tạp bổ sung này làm phức tạp thêm công việc tiếp theo với cơ sở dữ liệu này. Thật khó để hỗ trợ và hiểu.

Ở VictoriaMetrics, mọi thứ đơn giản hơn nhiều. Ở đó, mỗi chuỗi thời gian là một khóa-giá trị. Giá trị là một tập hợp các điểm - (timestamp, value), và chìa khóa là tập hợp label=value. Không có sự tách biệt giữa trường và phép đo. Nó cho phép bạn chọn bất kỳ dữ liệu nào và sau đó kết hợp, cộng, trừ, nhân, chia, không giống như InfluxDB nơi các phép tính giữa các hàng khác nhau vẫn chưa được triển khai theo như tôi biết. Ngay cả khi chúng được triển khai thì cũng khó khăn, bạn phải viết rất nhiều mã.

Tôi có một câu hỏi làm rõ. Tôi có hiểu chính xác rằng có một số vấn đề mà bạn đã nói đến, rằng chỉ mục đảo ngược này không vừa với bộ nhớ nên có sự phân vùng ở đó không?

Đầu tiên, tôi đã trình bày cách triển khai đơn giản về chỉ mục đảo ngược trên bản đồ cờ vây tiêu chuẩn. Việc triển khai này không phù hợp với cơ sở dữ liệu vì chỉ mục đảo ngược này không được lưu vào đĩa và cơ sở dữ liệu phải lưu vào đĩa để dữ liệu này vẫn có sẵn khi khởi động lại. Trong quá trình triển khai này, khi bạn khởi động lại ứng dụng, chỉ mục đảo ngược của bạn sẽ biến mất. Và bạn sẽ mất quyền truy cập vào tất cả dữ liệu vì bạn sẽ không thể tìm thấy nó.

Xin chào! Cảm ơn vì đã báo cáo! Tên tôi là Pavel. Tôi đến từ Wildberry. Tôi có một vài câu hỏi cho bạn. Câu hỏi một. Bạn có nghĩ rằng nếu bạn chọn một nguyên tắc khác khi xây dựng kiến ​​trúc ứng dụng của mình và phân vùng dữ liệu theo thời gian, thì có lẽ bạn đã có thể phân tách dữ liệu khi tìm kiếm, chỉ dựa trên thực tế là một phân vùng chứa dữ liệu cho một phân vùng. khoảng thời gian , tức là trong một khoảng thời gian và bạn sẽ không phải lo lắng về việc các mảnh của bạn nằm rải rác khác nhau? Câu hỏi số 2 - vì bạn đang triển khai một thuật toán tương tự với bitset và mọi thứ khác, nên có lẽ bạn đã thử sử dụng hướng dẫn của bộ xử lý? Có lẽ bạn đã thử tối ưu hóa như vậy?

Tôi sẽ trả lời câu thứ hai ngay. Chúng ta vẫn chưa đạt đến điểm đó. Nhưng nếu cần, chúng tôi sẽ đến đó. Và câu đầu tiên, câu hỏi là gì?

Bạn đã thảo luận về hai kịch bản. Và họ nói rằng họ đã chọn cái thứ hai với cách triển khai phức tạp hơn. Và họ không thích cái đầu tiên, nơi dữ liệu được phân chia theo thời gian.

Đúng. Trong trường hợp đầu tiên, tổng khối lượng của chỉ mục sẽ lớn hơn, vì trong mỗi phân vùng, chúng tôi sẽ phải lưu trữ dữ liệu trùng lặp cho các chuỗi thời gian tiếp tục qua tất cả các phân vùng này. Và nếu tốc độ rời bỏ chuỗi thời gian của bạn nhỏ, tức là cùng một chuỗi được sử dụng liên tục, thì trong trường hợp đầu tiên, chúng tôi sẽ mất nhiều dung lượng ổ đĩa bị chiếm dụng hơn so với trường hợp thứ hai.

Và vì vậy - vâng, phân vùng thời gian là một lựa chọn tốt. Prometheus sử dụng nó. Nhưng Prometheus còn có một nhược điểm khác. Khi hợp nhất các phần dữ liệu này, nó cần lưu giữ thông tin meta bộ nhớ cho tất cả các nhãn và chuỗi thời gian. Do đó, nếu các phần dữ liệu được hợp nhất có kích thước lớn thì mức tiêu thụ bộ nhớ sẽ tăng lên rất nhiều trong quá trình hợp nhất, không giống như VictoriaMetrics. Khi hợp nhất, VictoriaMetrics hoàn toàn không tiêu tốn bộ nhớ; chỉ tiêu thụ một vài kilobyte, bất kể kích thước của các phần dữ liệu được hợp nhất.

Thuật toán bạn đang sử dụng sử dụng bộ nhớ. Nó đánh dấu các thẻ chuỗi thời gian có chứa các giá trị. Và bằng cách này, bạn kiểm tra sự hiện diện được ghép nối trong một mảng dữ liệu và trong một mảng dữ liệu khác. Và bạn hiểu liệu giao lộ có xảy ra hay không. Thông thường, cơ sở dữ liệu triển khai các con trỏ và trình vòng lặp để lưu trữ nội dung hiện tại của chúng và chạy qua dữ liệu đã được sắp xếp do tính phức tạp đơn giản của các thao tác này.

Tại sao chúng ta không sử dụng con trỏ để duyệt dữ liệu?

Vâng.

Chúng tôi lưu trữ các hàng đã sắp xếp trong LevelDB hoặc mergeset. Chúng ta có thể di chuyển con trỏ và tìm giao điểm. Tại sao chúng ta không sử dụng nó? Bởi vì nó chậm. Bởi vì con trỏ có nghĩa là bạn cần gọi một hàm cho mỗi dòng. Một cuộc gọi hàm là 5 nano giây. Và nếu bạn có 100 dòng, thì hóa ra chúng ta dành nửa giây chỉ để gọi hàm.

Có một điều như vậy, vâng. Và câu hỏi cuối cùng của tôi. Câu hỏi nghe có vẻ hơi lạ. Tại sao không thể đọc tất cả các tập hợp cần thiết tại thời điểm dữ liệu đến và lưu chúng ở dạng bắt buộc? Tại sao phải lưu khối lượng lớn trong một số hệ thống như VictoriaMetrics, ClickHouse, v.v., rồi lại dành nhiều thời gian cho chúng?

Tôi sẽ đưa ra một ví dụ để làm cho nó rõ ràng hơn. Hãy nói xem đồng hồ tốc độ đồ chơi nhỏ hoạt động như thế nào? Nó ghi lại quãng đường bạn đã đi, luôn cộng nó vào một giá trị và lần thứ hai. Và chia rẽ. Và đạt được tốc độ trung bình. Bạn có thể làm điều tương tự. Thêm tất cả các sự kiện cần thiết một cách nhanh chóng.

Được rồi, tôi hiểu câu hỏi. Ví dụ của bạn có vị trí của nó. Nếu bạn biết mình cần tổng hợp gì thì đây là cách triển khai tốt nhất. Nhưng vấn đề là mọi người lưu các số liệu này, một số dữ liệu trong ClickHouse và họ chưa biết sau này sẽ tổng hợp và lọc chúng như thế nào nên phải lưu toàn bộ dữ liệu thô. Nhưng nếu bạn biết mình cần tính trung bình một cái gì đó, thì tại sao không tính toán nó thay vì lưu trữ một loạt giá trị thô ở đó? Nhưng điều này chỉ xảy ra nếu bạn biết chính xác những gì bạn cần.

Nhân tiện, cơ sở dữ liệu để lưu trữ chuỗi thời gian hỗ trợ tính tổng hợp. Ví dụ: Prometheus hỗ trợ quy tắc ghi âm. Nghĩa là, điều này có thể được thực hiện nếu bạn biết mình sẽ cần những đơn vị nào. VictoriaMetrics chưa có tính năng này, nhưng nó thường có trước Prometheus, trong đó điều này có thể được thực hiện trong các quy tắc mã hóa lại.

Ví dụ: trong công việc trước đây, tôi cần đếm số lượng sự kiện trong một cửa sổ trượt trong một giờ qua. Vấn đề là tôi phải thực hiện triển khai tùy chỉnh trong Go, tức là một dịch vụ để đếm thứ này. Dịch vụ này cuối cùng không hề tầm thường vì rất khó tính toán. Việc triển khai có thể đơn giản nếu bạn cần đếm một số tổng hợp ở những khoảng thời gian cố định. Nếu bạn muốn đếm các sự kiện trong một cửa sổ trượt thì việc này không đơn giản như bạn tưởng. Tôi nghĩ điều này vẫn chưa được triển khai trong ClickHouse hoặc trong cơ sở dữ liệu chuỗi thời gian vì nó khó thực hiện.

Và một câu hỏi nữa. Chúng tôi chỉ đang nói về mức trung bình và tôi nhớ rằng đã từng có một thứ như Graphite với phần phụ trợ Carbon. Và anh ấy biết cách làm mỏng dữ liệu cũ, nghĩa là để lại một điểm mỗi phút, một điểm mỗi giờ, v.v. Về nguyên tắc, điều này khá thuận tiện nếu chúng ta cần dữ liệu thô, nói một cách tương đối, trong một tháng và mọi thứ khác đều có thể được mỏng đi. Nhưng Prometheus và VictoriaMetrics không hỗ trợ chức năng này. Nó có kế hoạch để hỗ trợ nó? Nếu không, tai sao không?

Cảm ơn vì câu hỏi. Người dùng của chúng tôi hỏi câu hỏi này định kỳ. Họ hỏi khi nào chúng tôi sẽ thêm hỗ trợ cho việc lấy mẫu xuống. Có một số vấn đề ở đây. Thứ nhất, mọi người dùng đều hiểu downsampling điều gì đó khác biệt: ai đó muốn nhận bất kỳ điểm tùy ý nào trong một khoảng thời gian nhất định, ai đó muốn các giá trị tối đa, tối thiểu, trung bình. Nếu nhiều hệ thống ghi dữ liệu vào cơ sở dữ liệu của bạn thì bạn không thể gộp tất cả lại với nhau. Có thể mỗi hệ thống yêu cầu độ mỏng khác nhau. Và điều này rất khó thực hiện.

Và điều thứ hai là VictoriaMetrics, giống như ClickHouse, được tối ưu hóa để làm việc trên lượng lớn dữ liệu thô, do đó, nó có thể xử lý một tỷ dòng trong chưa đầy một giây nếu bạn có nhiều lõi trong hệ thống của mình. Quét các điểm theo chuỗi thời gian trong VictoriaMetrics – 50 điểm mỗi giây cho mỗi lõi. Và hiệu suất này sẽ thay đổi theo các lõi hiện có. Tức là, nếu bạn có 000 lõi chẳng hạn, bạn sẽ quét một tỷ điểm mỗi giây. Và đặc tính này của VictoriaMetrics và ClickHouse giúp giảm nhu cầu giảm mẫu.

Một tính năng khác là VictoriaMetrics nén dữ liệu này một cách hiệu quả. Mức nén trung bình trong quá trình sản xuất là từ 0,4 đến 0,8 byte mỗi điểm. Mỗi điểm là một dấu thời gian + giá trị. Và nó được nén trung bình thành ít hơn một byte.

Sergey. Tôi có một câu hỏi. Lượng thời gian ghi tối thiểu là bao nhiêu?

Một mili giây. Gần đây chúng tôi đã có cuộc trò chuyện với các nhà phát triển cơ sở dữ liệu chuỗi thời gian khác. Khoảng thời gian tối thiểu của họ là một giây. Và trong Graphite chẳng hạn, nó cũng là một giây. Trong OpenTSDB nó cũng là một giây. InfluxDB có độ chính xác nano giây. Trong VictoriaMetrics là một mili giây, vì ở Prometheus là một mili giây. Và VictoriaMetrics ban đầu được phát triển làm nơi lưu trữ từ xa cho Prometheus. Nhưng bây giờ nó có thể lưu dữ liệu từ các hệ thống khác.

Người mà tôi đã nói chuyện nói rằng họ có độ chính xác đến từng giây - đối với họ như vậy là đủ vì nó phụ thuộc vào loại dữ liệu đang được lưu trữ trong cơ sở dữ liệu chuỗi thời gian. Nếu đây là dữ liệu DevOps hoặc dữ liệu từ cơ sở hạ tầng, nơi bạn thu thập dữ liệu đó trong khoảng thời gian 30 giây mỗi phút, thì độ chính xác thứ hai là đủ, bạn không cần gì hơn. Và nếu bạn thu thập dữ liệu này từ các hệ thống giao dịch tần số cao thì bạn cần độ chính xác ở nano giây.

Độ chính xác đến mili giây trong VictoriaMetrics cũng phù hợp với trường hợp DevOps và có thể phù hợp với hầu hết các trường hợp mà tôi đã đề cập ở đầu báo cáo. Điều duy nhất có thể không phù hợp là hệ thống giao dịch tần số cao.

Cảm ơn! Và một câu hỏi khác. Khả năng tương thích trong PromQL là gì?

Khả năng tương thích ngược hoàn toàn. VictoriaMetrics hỗ trợ đầy đủ PromQL. Ngoài ra, nó còn bổ sung thêm chức năng nâng cao trong PromQL, được gọi là Số liệuQL. Có một cuộc nói chuyện trên YouTube về chức năng mở rộng này. Tôi đã phát biểu tại Cuộc gặp gỡ giám sát vào mùa xuân ở St. Petersburg.

Kênh Telegram VictoriaSố liệu.

Chỉ những người dùng đã đăng ký mới có thể tham gia khảo sát. Đăng nhập, xin vui lòng.

Điều gì đang ngăn cản bạn chuyển sang VictoriaMetrics làm nơi lưu trữ lâu dài cho Prometheus? (Viết bình luận, mình sẽ thêm vào bình chọn))

  • 71,4%Tôi không sử dụng Prometheus5

  • 28,6%Không biết về VictoriaMetrics2

7 người dùng bình chọn. 12 người dùng bỏ phiếu trắng.

Nguồn: www.habr.com

Thêm một lời nhận xét