Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Σας προτείνω να διαβάσετε τη μεταγραφή της αναφοράς στα τέλη του 2019 από τον Alexander Valyalkin "Βελτιστοποιήσεις μετάβασης στο VictoriaMetrics"

VictoriaMetrics — ένα γρήγορο και επεκτάσιμο ΣΔΒΔ για την αποθήκευση και την επεξεργασία δεδομένων με τη μορφή χρονοσειράς (η εγγραφή σχηματίζει χρόνο και ένα σύνολο τιμών που αντιστοιχούν σε αυτόν τον χρόνο, για παράδειγμα, που λαμβάνονται μέσω περιοδικής δημοσκόπησης της κατάστασης των αισθητήρων ή συλλογής μετρήσεις).

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Εδώ είναι ένας σύνδεσμος για το βίντεο αυτής της έκθεσης - https://youtu.be/MZ5P21j_HLE

Διαφάνειες

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Μιλησε μας για τον εαυτο σου. Είμαι ο Alexander Valyalkin. Εδώ ο λογαριασμός μου στο GitHub. Είμαι παθιασμένος με το Go και τη βελτιστοποίηση απόδοσης. Έγραψα πολλές χρήσιμες και όχι τόσο χρήσιμες βιβλιοθήκες. Ξεκινούν με το ένα από τα δύο fast, ή με quick πρόθεμα.

Αυτήν τη στιγμή εργάζομαι στη VictoriaMetrics. Τι είναι και τι κάνω εκεί; Θα μιλήσω για αυτό σε αυτήν την παρουσίαση.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Το περίγραμμα της έκθεσης έχει ως εξής:

  • Αρχικά, θα σας πω τι είναι το VictoriaMetrics.
  • Μετά θα σας πω τι είναι οι χρονολογικές σειρές.
  • Στη συνέχεια, θα σας πω πώς λειτουργεί μια βάση δεδομένων χρονοσειρών.
  • Στη συνέχεια, θα σας πω για την αρχιτεκτονική της βάσης δεδομένων: από τι αποτελείται.
  • Και μετά ας προχωρήσουμε στις βελτιστοποιήσεις που έχει η VictoriaMetrics. Αυτή είναι μια βελτιστοποίηση για τον ανεστραμμένο ευρετήριο και μια βελτιστοποίηση για την υλοποίηση του συνόλου bits στο Go.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Γνωρίζει κανείς από το κοινό τι είναι το VictoriaMetrics; Ουάου, πολύς κόσμος ήδη ξέρει. Είναι ένα καλό νέο. Για όσους δεν γνωρίζουν, αυτή είναι μια βάση δεδομένων χρονοσειρών. Βασίζεται στην αρχιτεκτονική ClickHouse, σε ορισμένες λεπτομέρειες της εφαρμογής ClickHouse. Για παράδειγμα, σε όπως: MergeTree, παράλληλος υπολογισμός σε όλους τους διαθέσιμους πυρήνες επεξεργαστή και βελτιστοποίηση απόδοσης με εργασία σε μπλοκ δεδομένων που τοποθετούνται στην κρυφή μνήμη του επεξεργαστή.

Η VictoriaMetrics παρέχει καλύτερη συμπίεση δεδομένων από άλλες βάσεις δεδομένων χρονοσειρών.

Κλιμακώνεται κάθετα - δηλαδή, μπορείτε να προσθέσετε περισσότερους επεξεργαστές, περισσότερη μνήμη RAM σε έναν υπολογιστή. Η VictoriaMetrics θα χρησιμοποιήσει με επιτυχία αυτούς τους διαθέσιμους πόρους και θα βελτιώσει τη γραμμική παραγωγικότητα.

Το VictoriaMetrics κλιμακώνεται επίσης οριζόντια - δηλαδή, μπορείτε να προσθέσετε επιπλέον κόμβους στο σύμπλεγμα VictoriaMetrics και η απόδοσή του θα αυξηθεί σχεδόν γραμμικά.

Όπως μαντέψατε, το VictoriaMetrics είναι μια γρήγορη βάση δεδομένων, γιατί δεν μπορώ να γράψω άλλα. Και είναι γραμμένο στο Go, οπότε το συζητώ σε αυτή τη συνάντηση.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ποιος ξέρει τι είναι η χρονοσειρά; Ξέρει επίσης πολύ κόσμο. Μια χρονοσειρά είναι μια σειρά από ζεύγη (timestamp, значение), όπου αυτά τα ζεύγη ταξινομούνται κατά χρόνο. Η τιμή είναι ένας αριθμός κινητής υποδιαστολής – float64.

Κάθε χρονοσειρά προσδιορίζεται μοναδικά από ένα κλειδί. Από τι αποτελείται αυτό το κλειδί; Αποτελείται από ένα μη κενό σύνολο ζευγών κλειδιών-τιμών.

Εδώ είναι ένα παράδειγμα χρονοσειράς. Το κλειδί αυτής της σειράς είναι μια λίστα με ζεύγη: __name__="cpu_usage" είναι το όνομα της μέτρησης, instance="my-server" - αυτός είναι ο υπολογιστής στον οποίο συλλέγεται αυτή η μέτρηση, datacenter="us-east" - αυτό είναι το κέντρο δεδομένων όπου βρίσκεται αυτός ο υπολογιστής.

Καταλήξαμε σε ένα όνομα χρονοσειράς που αποτελείται από τρία ζεύγη κλειδιών-τιμών. Αυτό το κλειδί αντιστοιχεί σε μια λίστα ζευγών (timestamp, value). t1, t3, t3, ..., tN - αυτές είναι χρονικές σημάνσεις, 10, 20, 12, ..., 15 — τις αντίστοιχες τιμές. Αυτή είναι η χρήση της cpu σε μια δεδομένη στιγμή για μια δεδομένη σειρά.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Πού μπορούν να χρησιμοποιηθούν χρονοσειρές; Έχει κανείς καμιά ιδέα;

  • Στο DevOps, μπορείτε να μετρήσετε CPU, RAM, δίκτυο, rps, αριθμό σφαλμάτων κ.λπ.
  • IoT - μπορούμε να μετρήσουμε τη θερμοκρασία, την πίεση, τις γεωγραφικές συντεταγμένες και κάτι άλλο.
  • Επίσης χρηματοδότηση – μπορούμε να παρακολουθούμε τις τιμές για όλα τα είδη μετοχών και νομισμάτων.
  • Επιπλέον, οι χρονοσειρές μπορούν να χρησιμοποιηθούν για την παρακολούθηση των διαδικασιών παραγωγής στα εργοστάσια. Έχουμε χρήστες που χρησιμοποιούν το VictoriaMetrics για την παρακολούθηση ανεμογεννητριών, για ρομπότ.
  • Οι χρονοσειρές είναι επίσης χρήσιμες για τη συλλογή πληροφοριών από αισθητήρες διαφόρων συσκευών. Για παράδειγμα, για έναν κινητήρα? για τη μέτρηση της πίεσης των ελαστικών? για μέτρηση ταχύτητας, απόστασης. για τη μέτρηση της κατανάλωσης βενζίνης κ.λπ.
  • Οι χρονοσειρές μπορούν επίσης να χρησιμοποιηθούν για την παρακολούθηση αεροσκαφών. Κάθε αεροσκάφος έχει ένα μαύρο κουτί που συλλέγει χρονοσειρές για διάφορες παραμέτρους της υγείας του αεροσκάφους. Οι χρονοσειρές χρησιμοποιούνται επίσης στην αεροδιαστημική βιομηχανία.
  • Η υγειονομική περίθαλψη είναι η αρτηριακή πίεση, ο σφυγμός κ.λπ.

Μπορεί να υπάρχουν περισσότερες εφαρμογές που ξέχασα, αλλά ελπίζω να καταλαβαίνετε ότι οι χρονοσειρές χρησιμοποιούνται ενεργά στον σύγχρονο κόσμο. Και ο όγκος της χρήσης τους αυξάνεται κάθε χρόνο.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Γιατί χρειάζεστε μια βάση δεδομένων χρονοσειρών; Γιατί δεν μπορείτε να χρησιμοποιήσετε μια κανονική σχεσιακή βάση δεδομένων για την αποθήκευση χρονοσειρών;

Επειδή οι χρονοσειρές συνήθως περιέχουν μεγάλο όγκο πληροφοριών, οι οποίες είναι δύσκολο να αποθηκευτούν και να επεξεργαστούν σε συμβατικές βάσεις δεδομένων. Ως εκ τούτου, εμφανίστηκαν εξειδικευμένες βάσεις δεδομένων για χρονοσειρές. Αυτές οι βάσεις αποθηκεύουν αποτελεσματικά σημεία (timestamp, value) με το δοσμένο κλειδί. Παρέχουν ένα API για την ανάγνωση αποθηκευμένων δεδομένων ανά κλειδί, με ένα μόνο ζεύγος κλειδιού-τιμής ή με πολλαπλά ζεύγη κλειδιού-τιμής ή με regexp. Για παράδειγμα, θέλετε να βρείτε το φορτίο CPU όλων των υπηρεσιών σας σε ένα κέντρο δεδομένων στην Αμερική, τότε πρέπει να χρησιμοποιήσετε αυτό το ψευδοερώτημα.

Συνήθως οι βάσεις δεδομένων χρονοσειρών παρέχουν εξειδικευμένες γλώσσες ερωτημάτων, επειδή η SQL χρονοσειρών δεν είναι πολύ κατάλληλη. Αν και υπάρχουν βάσεις δεδομένων που υποστηρίζουν SQL, δεν είναι πολύ κατάλληλη. Γλώσσες ερωτήσεων όπως π.χ PromQL, InfluxQL, Ροή, Q. Ελπίζω ότι κάποιος έχει ακούσει τουλάχιστον μία από αυτές τις γλώσσες. Πολλοί άνθρωποι πιθανότατα έχουν ακούσει για το PromQL. Αυτή είναι η γλώσσα ερωτημάτων Prometheus.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Έτσι μοιάζει μια σύγχρονη αρχιτεκτονική βάσης δεδομένων χρονοσειρών χρησιμοποιώντας το VictoriaMetrics ως παράδειγμα.

Αποτελείται από δύο μέρη. Αυτή είναι η αποθήκευση για τον ανεστραμμένο δείκτη και η αποθήκευση για τις τιμές χρονοσειρών. Αυτά τα αποθετήρια είναι χωρισμένα.

Όταν μια νέα εγγραφή φτάνει στη βάση δεδομένων, έχουμε πρώτα πρόσβαση στον ανεστραμμένο ευρετήριο για να βρούμε το αναγνωριστικό χρονοσειράς για ένα δεδομένο σύνολο label=value για μια δεδομένη μέτρηση. Βρίσκουμε αυτό το αναγνωριστικό και αποθηκεύουμε την τιμή στο χώρο αποθήκευσης δεδομένων.

Όταν έρχεται ένα αίτημα για ανάκτηση δεδομένων από το TSDB, πηγαίνουμε πρώτα στον ανεστραμμένο ευρετήριο. Ας τα πάρουμε όλα timeseries_ids ρεκόρ που ταιριάζουν με αυτό το σύνολο label=value. Και τότε λαμβάνουμε όλα τα απαραίτητα δεδομένα από την αποθήκη δεδομένων, ευρετηριασμένα από timeseries_ids.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ας δούμε ένα παράδειγμα του τρόπου με τον οποίο μια βάση δεδομένων χρονοσειρών επεξεργάζεται ένα εισερχόμενο ερώτημα επιλογής.

  • Πρώτα από όλα τα παίρνει όλα timeseries_ids από έναν ανεστραμμένο δείκτη που περιέχει τα δεδομένα ζεύγη label=value, ή να ικανοποιήσει μια δεδομένη κανονική έκφραση.
  • Στη συνέχεια ανακτά όλα τα σημεία δεδομένων από την αποθήκευση δεδομένων σε ένα δεδομένο χρονικό διάστημα για αυτά που βρέθηκαν timeseries_ids.
  • Μετά από αυτό, η βάση δεδομένων εκτελεί ορισμένους υπολογισμούς σε αυτά τα σημεία δεδομένων, σύμφωνα με το αίτημα του χρήστη. Και μετά επιστρέφει την απάντηση.

Σε αυτή την παρουσίαση θα σας μιλήσω για το πρώτο μέρος. Αυτό είναι μια αναζήτηση timeseries_ids με ανεστραμμένο δείκτη. Μπορείτε να παρακολουθήσετε το δεύτερο μέρος και το τρίτο μέρος αργότερα Πηγές VictoriaMetrics, ή περιμένετε μέχρι να ετοιμάσω άλλες αναφορές :)

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ας προχωρήσουμε στον ανεστραμμένο δείκτη. Πολλοί μπορεί να πιστεύουν ότι αυτό είναι απλό. Ποιος ξέρει τι είναι ένας ανεστραμμένος δείκτης και πώς λειτουργεί; Α, όχι τόσος κόσμος πια. Ας προσπαθήσουμε να καταλάβουμε τι είναι.

Στην πραγματικότητα είναι απλό. Είναι απλώς ένα λεξικό που αντιστοιχίζει ένα κλειδί σε μια τιμή. Τι είναι ένα κλειδί; Αυτό το ζευγάρι label=valueΌπου label и value - αυτές είναι γραμμές. Και οι αξίες είναι ένα σύνολο timeseries_ids, που περιλαμβάνει το δεδομένο ζεύγος label=value.

Το ανεστραμμένο ευρετήριο σάς επιτρέπει να βρίσκετε γρήγορα τα πάντα timeseries_ids, που έχουν δώσει label=value.

Σας επιτρέπει επίσης να βρίσκετε γρήγορα timeseries_ids χρονοσειρές για πολλά ζεύγη label=value, ή για ζευγάρια label=regexp. Πώς συμβαίνει αυτό; Με την εύρεση της τομής του συνόλου timeseries_ids για κάθε ζευγάρι label=value.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ας δούμε διάφορες υλοποιήσεις του ανεστραμμένου ευρετηρίου. Ας ξεκινήσουμε με την πιο απλή αφελή υλοποίηση. Μοιάζει κάπως έτσι.

Λειτουργία getMetricIDs λαμβάνει μια λίστα με συμβολοσειρές. Κάθε γραμμή περιέχει label=value. Αυτή η συνάρτηση επιστρέφει μια λίστα metricIDs.

Πως δουλεύει? Εδώ έχουμε μια καθολική μεταβλητή που ονομάζεται invertedIndex. Αυτό είναι ένα κανονικό λεξικό (map), το οποίο θα αντιστοιχίσει τη συμβολοσειρά για να τεμαχίσει ints. Η γραμμή περιέχει label=value.

Εφαρμογή συνάρτησης: πάρτε metricIDs για τον πρώτο label=value, μετά περνάμε από όλα τα άλλα label=value, καταλαβαίνουμε metricIDs για αυτούς. Και καλέστε τη συνάρτηση intersectInts, το οποίο θα συζητηθεί παρακάτω. Και αυτή η συνάρτηση επιστρέφει την τομή αυτών των λιστών.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Όπως μπορείτε να δείτε, η υλοποίηση ενός ανεστραμμένου ευρετηρίου δεν είναι πολύ περίπλοκη. Αλλά αυτό είναι μια αφελής υλοποίηση. Τι μειονεκτήματα έχει; Το κύριο μειονέκτημα της απλής υλοποίησης είναι ότι ένας τέτοιος ανεστραμμένος δείκτης αποθηκεύεται στη μνήμη RAM. Μετά την επανεκκίνηση της εφαρμογής χάνουμε αυτό το ευρετήριο. Δεν υπάρχει αποθήκευση αυτού του ευρετηρίου στο δίσκο. Ένα τέτοιο ανεστραμμένο ευρετήριο είναι απίθανο να είναι κατάλληλο για μια βάση δεδομένων.

Το δεύτερο μειονέκτημα σχετίζεται επίσης με τη μνήμη. Ο ανεστραμμένος δείκτης πρέπει να ταιριάζει στη μνήμη RAM. Εάν υπερβαίνει το μέγεθος της μνήμης RAM, τότε προφανώς θα έχουμε σφάλμα - από τη μνήμη. Και το πρόγραμμα δεν θα λειτουργήσει.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Αυτό το πρόβλημα μπορεί να λυθεί χρησιμοποιώντας έτοιμες λύσεις όπως π.χ LevelDBΉ RocksDB.

Εν ολίγοις, χρειαζόμαστε μια βάση δεδομένων που μας επιτρέπει να κάνουμε τρεις λειτουργίες γρήγορα.

  • Η πρώτη λειτουργία είναι η εγγραφή ключ-значение σε αυτή τη βάση δεδομένων. Το κάνει πολύ γρήγορα, όπου ключ-значение είναι αυθαίρετες χορδές.
  • Η δεύτερη λειτουργία είναι μια γρήγορη αναζήτηση μιας τιμής χρησιμοποιώντας ένα δεδομένο κλειδί.
  • Και η τρίτη λειτουργία είναι μια γρήγορη αναζήτηση για όλες τις τιμές με ένα δεδομένο πρόθεμα.

LevelDB και RocksDB - αυτές οι βάσεις δεδομένων αναπτύχθηκαν από την Google και το Facebook. Πρώτα ήρθε το LevelDB. Μετά τα παιδιά από το Facebook πήραν το LevelDB και άρχισαν να το βελτιώνουν, έφτιαξαν το RocksDB. Τώρα σχεδόν όλες οι εσωτερικές βάσεις δεδομένων λειτουργούν στο RocksDB μέσα στο Facebook, συμπεριλαμβανομένων εκείνων που έχουν μεταφερθεί στο RocksDB και στο MySQL. Τον ονόμασαν MyRocks.

Ένα ανεστραμμένο ευρετήριο μπορεί να υλοποιηθεί χρησιμοποιώντας το LevelDB. Πως να το κάνεις? Αποθηκεύουμε ως κλειδί label=value. Και η τιμή είναι το αναγνωριστικό της χρονοσειράς όπου υπάρχει το ζεύγος label=value.

Αν έχουμε πολλές χρονοσειρές με ένα δεδομένο ζεύγος label=value, τότε θα υπάρχουν πολλές σειρές σε αυτήν τη βάση δεδομένων με το ίδιο κλειδί και διαφορετικές timeseries_ids. Για να πάρετε μια λίστα με όλα timeseries_ids, που ξεκινούν με αυτό label=prefix, κάνουμε μια σάρωση εύρους για την οποία αυτή η βάση δεδομένων είναι βελτιστοποιημένη. Δηλαδή, επιλέγουμε όλες τις γραμμές που ξεκινούν με label=prefix και πάρτε τα απαραίτητα timeseries_ids.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ακολουθεί ένα δείγμα υλοποίησης του πώς θα φαινόταν στο Go. Έχουμε έναν ανεστραμμένο δείκτη. Αυτό είναι το LevelDB.

Η λειτουργία είναι η ίδια με την αφελή υλοποίηση. Επαναλαμβάνει την αφελή υλοποίηση σχεδόν γραμμή προς γραμμή. Το μόνο σημείο είναι ότι αντί να στραφούμε σε map έχουμε πρόσβαση στον ανεστραμμένο δείκτη. Λαμβάνουμε όλες τις τιμές για το πρώτο label=value. Στη συνέχεια περνάμε από όλα τα υπόλοιπα ζευγάρια label=value και λάβετε τα αντίστοιχα σύνολα metricIDs για αυτά. Στη συνέχεια βρίσκουμε τη διασταύρωση.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Όλα φαίνονται να είναι καλά, αλλά υπάρχουν μειονεκτήματα σε αυτή τη λύση. Η VictoriaMetrics αρχικά εφάρμοσε έναν ανεστραμμένο δείκτη βασισμένο στο LevelDB. Αλλά στο τέλος έπρεπε να το παρατήσω.

Γιατί; Επειδή το LevelDB είναι πιο αργό από την απλή υλοποίηση. Σε μια αφελή υλοποίηση, με δεδομένο ένα κλειδί, ανακτούμε αμέσως ολόκληρη τη φέτα metricIDs. Αυτή είναι μια πολύ γρήγορη λειτουργία - ολόκληρη η φέτα είναι έτοιμη για χρήση.

Στο LevelDB, κάθε φορά που καλείται μια συνάρτηση GetValues πρέπει να περάσετε από όλες τις γραμμές που ξεκινούν με label=value. Και λάβετε την τιμή για κάθε γραμμή timeseries_ids. Από τέτοια timeseries_ids συλλέξτε ένα κομμάτι από αυτά timeseries_ids. Προφανώς, αυτό είναι πολύ πιο αργό από την απλή πρόσβαση σε έναν κανονικό χάρτη με το κλειδί.

Το δεύτερο μειονέκτημα είναι ότι το LevelDB είναι γραμμένο σε C. Η κλήση συναρτήσεων C από το Go δεν είναι πολύ γρήγορη. Χρειάζονται εκατοντάδες νανοδευτερόλεπτα. Αυτό δεν είναι πολύ γρήγορο, γιατί σε σύγκριση με μια κανονική κλήση συνάρτησης γραμμένη in go, η οποία διαρκεί 1-5 νανοδευτερόλεπτα, η διαφορά στην απόδοση είναι δεκάδες φορές. Για τη VictoriaMetrics αυτό ήταν ένα μοιραίο ελάττωμα :)

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Έτσι έγραψα τη δική μου υλοποίηση του ανεστραμμένου δείκτη. Και της τηλεφώνησε συγχώνευση.

Το Mergeset βασίζεται στη δομή δεδομένων MergeTree. Αυτή η δομή δεδομένων είναι δανεισμένη από το ClickHouse. Προφανώς, το mergeset θα πρέπει να βελτιστοποιηθεί για γρήγορη αναζήτηση timeseries_ids σύμφωνα με το δεδομένο κλειδί. Το Mergeset είναι γραμμένο εξ ολοκλήρου στο Go. Μπορείς να δεις Πηγές VictoriaMetrics στο GitHub. Η υλοποίηση του mergeset βρίσκεται στον φάκελο /lib/mergeset. Μπορείτε να προσπαθήσετε να καταλάβετε τι συμβαίνει εκεί.

Το mergeset API είναι πολύ παρόμοιο με το LevelDB και το RocksDB. Δηλαδή, σας επιτρέπει να αποθηκεύετε γρήγορα νέες εγγραφές εκεί και να επιλέγετε γρήγορα εγγραφές με ένα δεδομένο πρόθεμα.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Θα μιλήσουμε για τα μειονεκτήματα του mergeset αργότερα. Τώρα ας μιλήσουμε για τα προβλήματα που προέκυψαν με τη VictoriaMetrics στην παραγωγή κατά την εφαρμογή ενός ανεστραμμένου δείκτη.

Γιατί προέκυψαν;

Ο πρώτος λόγος είναι ο υψηλός ρυθμός ανατροπής. Μεταφρασμένο στα ρωσικά, αυτή είναι μια συχνή αλλαγή στις χρονοσειρές. Αυτό συμβαίνει όταν τελειώνει μια χρονοσειρά και ξεκινά μια νέα σειρά ή ξεκινούν πολλές νέες χρονοσειρές. Και αυτό συμβαίνει συχνά.

Ο δεύτερος λόγος είναι ο μεγάλος αριθμός χρονοσειρών. Στην αρχή, όταν η παρακολούθηση κέρδιζε δημοτικότητα, ο αριθμός των χρονοσειρών ήταν μικρός. Για παράδειγμα, για κάθε υπολογιστή πρέπει να παρακολουθείτε το φορτίο της CPU, της μνήμης, του δικτύου και του δίσκου. 4 χρονοσειρές ανά υπολογιστή. Ας υποθέσουμε ότι έχετε 100 υπολογιστές και 400 χρονοσειρές. Αυτό είναι πολύ λίγο.

Με τον καιρό, οι άνθρωποι κατάλαβαν ότι μπορούσαν να μετρήσουν πιο αναλυτικές πληροφορίες. Για παράδειγμα, μετρήστε το φορτίο όχι ολόκληρου του επεξεργαστή, αλλά ξεχωριστά κάθε πυρήνα επεξεργαστή. Εάν έχετε 40 πυρήνες επεξεργαστή, τότε έχετε 40 φορές περισσότερες χρονοσειρές για τη μέτρηση του φορτίου του επεξεργαστή.

Αλλά δεν είναι μόνο αυτό. Κάθε πυρήνας επεξεργαστή μπορεί να έχει πολλές καταστάσεις, όπως idle, όταν είναι αδρανής. Και επίσης εργασία στο χώρο χρήστη, εργασία σε χώρο πυρήνα και άλλες καταστάσεις. Και κάθε τέτοια κατάσταση μπορεί επίσης να μετρηθεί ως ξεχωριστή χρονοσειρά. Αυτό αυξάνει επιπλέον τον αριθμό των σειρών κατά 7-8 φορές.

Από μία μέτρηση πήραμε 40 x 8 = 320 μετρήσεις για έναν μόνο υπολογιστή. Πολλαπλασιάζοντας με το 100, παίρνουμε 32 αντί για 000.

Μετά ήρθε ο Kubernetes. Και έγινε χειρότερο επειδή το Kubernetes μπορεί να φιλοξενήσει πολλές διαφορετικές υπηρεσίες. Κάθε υπηρεσία στο Kubernetes αποτελείται από πολλά pods. Και όλα αυτά πρέπει να παρακολουθούνται. Επιπλέον, έχουμε συνεχή ανάπτυξη νέων εκδόσεων των υπηρεσιών σας. Για κάθε νέα έκδοση, πρέπει να δημιουργούνται νέες χρονοσειρές. Ως αποτέλεσμα, ο αριθμός των χρονοσειρών αυξάνεται εκθετικά και αντιμετωπίζουμε το πρόβλημα ενός μεγάλου αριθμού χρονοσειρών, το οποίο ονομάζεται high-cardinality. Η VictoriaMetrics το αντιμετωπίζει με επιτυχία σε σύγκριση με άλλες βάσεις δεδομένων χρονοσειρών.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ας ρίξουμε μια πιο προσεκτική ματιά στον υψηλό ρυθμό εκτροπής. Τι προκαλεί τον υψηλό ρυθμό ανατροπής στην παραγωγή; Επειδή ορισμένες έννοιες των ετικετών και των ετικετών αλλάζουν συνεχώς.

Για παράδειγμα, πάρτε το Kubernetes, το οποίο έχει την έννοια deployment, δηλαδή όταν κυκλοφορήσει μια νέα έκδοση της εφαρμογής σας. Για κάποιο λόγο, οι προγραμματιστές του Kubernetes αποφάσισαν να προσθέσουν το αναγνωριστικό ανάπτυξης στην ετικέτα.

Σε τι οδήγησε αυτό; Επιπλέον, με κάθε νέα ανάπτυξη, διακόπτονται όλες οι παλιές χρονοσειρές και αντί για αυτές, οι νέες χρονοσειρές ξεκινούν με μια νέα τιμή ετικέτας deployment_id. Μπορεί να υπάρχουν εκατοντάδες χιλιάδες, ακόμη και εκατομμύρια τέτοιες σειρές.

Το σημαντικό με όλα αυτά είναι ότι ο συνολικός αριθμός των χρονοσειρών αυξάνεται, αλλά ο αριθμός των χρονοσειρών που είναι ενεργές αυτήν τη στιγμή και λαμβάνουν δεδομένα παραμένει σταθερός. Αυτή η κατάσταση ονομάζεται υψηλός ρυθμός εκτροπής.

Το κύριο πρόβλημα του υψηλού ρυθμού εκτροπής είναι η διασφάλιση σταθερής ταχύτητας αναζήτησης για όλες τις χρονικές σειρές για ένα δεδομένο σύνολο ετικετών σε ένα συγκεκριμένο χρονικό διάστημα. Συνήθως αυτό είναι το χρονικό διάστημα για την τελευταία ώρα ή την τελευταία ημέρα.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Πώς να λύσετε αυτό το πρόβλημα; Εδώ είναι η πρώτη επιλογή. Αυτό γίνεται για να διαιρέσουμε τον ανεστραμμένο δείκτη σε ανεξάρτητα μέρη με την πάροδο του χρόνου. Δηλαδή, περνάει κάποιο χρονικό διάστημα, τελειώνουμε την εργασία με τον τρέχοντα ανεστραμμένο δείκτη. Και δημιουργήστε ένα νέο ανεστραμμένο ευρετήριο. Άλλο χρονικό διάστημα περνά, άλλο δημιουργούμε κι άλλο.

Και όταν κάνουμε δειγματοληψία από αυτούς τους ανεστραμμένους δείκτες, βρίσκουμε ένα σύνολο ανεστραμμένων δεικτών που εμπίπτουν στο δεδομένο διάστημα. Και, ανάλογα, επιλέγουμε το id της χρονοσειράς από εκεί.

Αυτό εξοικονομεί πόρους επειδή δεν χρειάζεται να εξετάσουμε μέρη που δεν εμπίπτουν στο δεδομένο διάστημα. Δηλαδή, συνήθως, αν επιλέξουμε δεδομένα για την τελευταία ώρα, τότε για προηγούμενα χρονικά διαστήματα παρακάμπτουμε τα ερωτήματα.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Υπάρχει μια άλλη επιλογή για την επίλυση αυτού του προβλήματος. Αυτό γίνεται για να αποθηκεύεται για κάθε ημέρα μια ξεχωριστή λίστα αναγνωριστικών χρονοσειρών που εμφανίστηκαν εκείνη την ημέρα.

Το πλεονέκτημα αυτής της λύσης σε σχέση με την προηγούμενη λύση είναι ότι δεν αντιγράφουμε πληροφορίες χρονοσειρών που δεν εξαφανίζονται με την πάροδο του χρόνου. Είναι συνεχώς παρόντες και δεν αλλάζουν.

Το μειονέκτημα είναι ότι μια τέτοια λύση είναι πιο δύσκολη στην εφαρμογή και πιο δύσκολη στον εντοπισμό σφαλμάτων. Και η VictoriaMetrics επέλεξε αυτή τη λύση. Έτσι έγινε ιστορικά. Αυτή η λύση έχει επίσης καλή απόδοση σε σύγκριση με την προηγούμενη. Επειδή αυτή η λύση δεν εφαρμόστηκε λόγω του γεγονότος ότι είναι απαραίτητο να αντιγραφούν δεδομένα σε κάθε διαμέρισμα για χρονοσειρές που δεν αλλάζουν, δηλαδή δεν εξαφανίζονται με την πάροδο του χρόνου. Το VictoriaMetrics βελτιστοποιήθηκε κυρίως για κατανάλωση χώρου στο δίσκο και η προηγούμενη εφαρμογή έκανε χειρότερη την κατανάλωση χώρου στο δίσκο. Αλλά αυτή η υλοποίηση είναι πιο κατάλληλη για την ελαχιστοποίηση της κατανάλωσης χώρου στο δίσκο, γι' αυτό επιλέχθηκε.

Έπρεπε να την πολεμήσω. Ο αγώνας ήταν ότι σε αυτήν την εφαρμογή πρέπει ακόμα να επιλέξετε έναν πολύ μεγαλύτερο αριθμό timeseries_ids για δεδομένα παρά όταν ο ανεστραμμένος δείκτης είναι χρονικά διαμερισμένος.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Πώς λύσαμε αυτό το πρόβλημα; Το λύσαμε με έναν πρωτότυπο τρόπο - αποθηκεύοντας πολλά αναγνωριστικά χρονοσειρών σε κάθε καταχώρηση ανεστραμμένου ευρετηρίου αντί για ένα αναγνωριστικό. Δηλαδή έχουμε ένα κλειδί label=value, που εμφανίζεται σε κάθε χρονολογική σειρά. Και τώρα εξοικονομούμε αρκετά timeseries_ids σε μια καταχώρηση.

Εδώ είναι ένα παράδειγμα. Προηγουμένως είχαμε N καταχωρήσεις, αλλά τώρα έχουμε μία εγγραφή της οποίας το πρόθεμα είναι ίδιο με όλες τις άλλες. Για την προηγούμενη καταχώρηση, η τιμή περιέχει όλα τα αναγνωριστικά χρονικών σειρών.

Αυτό κατέστησε δυνατή την αύξηση της ταχύτητας σάρωσης ενός τέτοιου ανεστραμμένου δείκτη έως και 10 φορές. Και μας επέτρεψε να μειώσουμε την κατανάλωση μνήμης για τη μνήμη cache, γιατί τώρα αποθηκεύουμε τη συμβολοσειρά label=value μόνο μία φορά στην κρυφή μνήμη μαζί Ν φορές. Και αυτή η γραμμή μπορεί να είναι μεγάλη εάν αποθηκεύετε μεγάλες ουρές στις ετικέτες και τις ετικέτες σας, τις οποίες η Kubernetes θέλει να τις σπρώχνει εκεί.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Μια άλλη επιλογή για την επιτάχυνση της αναζήτησης σε ένα ανεστραμμένο ευρετήριο είναι η κοινή χρήση. Δημιουργία πολλών ανεστραμμένων ευρετηρίων αντί για ένα και κοινή χρήση δεδομένων μεταξύ τους με κλειδί. Αυτό είναι ένα σετ key=value ατμός. Δηλαδή, παίρνουμε αρκετούς ανεξάρτητους ανεστραμμένους δείκτες, τους οποίους μπορούμε να ρωτήσουμε παράλληλα σε πολλούς επεξεργαστές. Οι προηγούμενες υλοποιήσεις επέτρεπαν τη λειτουργία μόνο σε λειτουργία ενός επεξεργαστή, δηλαδή σάρωση δεδομένων μόνο σε έναν πυρήνα. Αυτή η λύση σάς επιτρέπει να σαρώνετε δεδομένα σε πολλούς πυρήνες ταυτόχρονα, όπως θέλει να κάνει το ClickHouse. Αυτό σκοπεύουμε να εφαρμόσουμε.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Τώρα ας επιστρέψουμε στα πρόβατά μας - στη συνάρτηση τομής timeseries_ids. Ας εξετάσουμε ποιες υλοποιήσεις μπορεί να υπάρχουν. Αυτή η λειτουργία σάς επιτρέπει να βρείτε timeseries_ids για ένα δεδομένο σύνολο label=value.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Η πρώτη επιλογή είναι μια αφελής υλοποίηση. Δύο ένθετες θηλιές. Εδώ παίρνουμε την είσοδο συνάρτησης intersectInts δύο φέτες - a и b. Στην έξοδο, θα πρέπει να μας επιστρέψει τη διασταύρωση αυτών των φετών.

Μια αφελής υλοποίηση μοιάζει με αυτό. Επαναλαμβάνουμε όλες τις τιμές από φέτα a, μέσα σε αυτόν τον βρόχο περνάμε από όλες τις τιμές του slice b. Και τα συγκρίνουμε. Αν ταιριάζουν, τότε έχουμε βρει μια διασταύρωση. Και αποθηκεύστε το result.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ποια είναι τα μειονεκτήματα; Η τετραγωνική πολυπλοκότητα είναι το κύριο μειονέκτημά του. Για παράδειγμα, αν οι διαστάσεις σας είναι φέτες a и b ένα εκατομμύριο κάθε φορά, τότε αυτή η συνάρτηση δεν θα σας επιστρέψει ποτέ μια απάντηση. Γιατί θα χρειαστεί να κάνει ένα τρισεκατομμύριο επαναλήψεις, κάτι που είναι πολύ ακόμη και για σύγχρονους υπολογιστές.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Η δεύτερη υλοποίηση βασίζεται σε χάρτη. Δημιουργούμε χάρτη. Βάζουμε όλες τις τιμές από το slice σε αυτόν τον χάρτη a. Στη συνέχεια περνάμε από φέτα σε ξεχωριστό βρόχο b. Και ελέγχουμε αν αυτή η τιμή είναι από το slice b στον χάρτη. Εάν υπάρχει, τότε προσθέστε το στο αποτέλεσμα.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ποια είναι τα οφέλη; Το πλεονέκτημα είναι ότι υπάρχει μόνο γραμμική πολυπλοκότητα. Δηλαδή, η συνάρτηση θα εκτελείται πολύ πιο γρήγορα για μεγαλύτερες φέτες. Για ένα κομμάτι μεγέθους εκατομμυρίου, αυτή η συνάρτηση θα εκτελεστεί σε 2 εκατομμύρια επαναλήψεις, σε αντίθεση με τα τρισεκατομμύρια επαναλήψεις της προηγούμενης συνάρτησης.

Το μειονέκτημα είναι ότι αυτή η λειτουργία απαιτεί περισσότερη μνήμη για τη δημιουργία αυτού του χάρτη.

Το δεύτερο μειονέκτημα είναι η μεγάλη επιβάρυνση για κατακερματισμό. Αυτό το μειονέκτημα δεν είναι πολύ προφανές. Και για εμάς δεν ήταν επίσης πολύ προφανές, οπότε αρχικά στο VictoriaMetrics η υλοποίηση της διασταύρωσης γινόταν μέσω χάρτη. Στη συνέχεια, όμως, το προφίλ έδειξε ότι ο κύριος χρόνος του επεξεργαστή ξοδεύεται για να γράψει στον χάρτη και να ελέγξει για την παρουσία μιας τιμής σε αυτόν τον χάρτη.

Γιατί χάνεται ο χρόνος της CPU σε αυτά τα μέρη; Επειδή το Go εκτελεί μια λειτουργία κατακερματισμού σε αυτές τις γραμμές. Δηλαδή, υπολογίζει τον κατακερματισμό του κλειδιού για να έχει πρόσβαση σε αυτό σε ένα δεδομένο ευρετήριο στο HashMap. Η λειτουργία υπολογισμού κατακερματισμού ολοκληρώνεται σε δεκάδες νανοδευτερόλεπτα. Αυτό είναι αργό για τη VictoriaMetrics.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Αποφάσισα να εφαρμόσω ένα bitset βελτιστοποιημένο ειδικά για αυτήν την περίπτωση. Έτσι μοιάζει τώρα η τομή δύο φετών. Εδώ δημιουργούμε ένα bitset. Σε αυτό προσθέτουμε στοιχεία από την πρώτη φέτα. Στη συνέχεια ελέγχουμε την παρουσία αυτών των στοιχείων στη δεύτερη φέτα. Και προσθέστε τα στο αποτέλεσμα. Δηλαδή, δεν διαφέρει σχεδόν καθόλου από το προηγούμενο παράδειγμα. Το μόνο πράγμα εδώ είναι ότι αντικαταστήσαμε την πρόσβαση στον χάρτη με προσαρμοσμένες λειτουργίες add и has.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Με την πρώτη ματιά, φαίνεται ότι αυτό θα πρέπει να λειτουργεί πιο αργά, εάν προηγουμένως χρησιμοποιήθηκε ένας τυπικός χάρτης εκεί και στη συνέχεια καλούνται ορισμένες άλλες λειτουργίες, αλλά το προφίλ δείχνει ότι αυτό το πράγμα λειτουργεί 10 φορές πιο γρήγορα από τον τυπικό χάρτη στην περίπτωση της VictoriaMetrics.

Επιπλέον, χρησιμοποιεί πολύ λιγότερη μνήμη σε σύγκριση με την υλοποίηση του χάρτη. Επειδή αποθηκεύουμε bits εδώ αντί για τιμές οκτώ byte.

Το μειονέκτημα αυτής της υλοποίησης είναι ότι δεν είναι τόσο προφανές, όχι ασήμαντο.

Ένα άλλο μειονέκτημα που πολλοί μπορεί να μην παρατηρήσουν είναι ότι αυτή η υλοποίηση μπορεί να μην λειτουργεί καλά σε ορισμένες περιπτώσεις. Δηλαδή, είναι βελτιστοποιημένο για μια συγκεκριμένη περίπτωση, για αυτήν την περίπτωση τομής αναγνωριστικών χρονοσειρών VictoriaMetrics. Αυτό δεν σημαίνει ότι είναι κατάλληλο για όλες τις περιπτώσεις. Εάν χρησιμοποιηθεί λανθασμένα, δεν θα έχουμε αύξηση απόδοσης, αλλά σφάλμα εξαντλήσεως μνήμης και επιβράδυνση της απόδοσης.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ας εξετάσουμε την εφαρμογή αυτής της δομής. Αν θέλετε να κοιτάξετε, βρίσκεται στις πηγές VictoriaMetrics, στο φάκελο lib/uint64set. Έχει βελτιστοποιηθεί ειδικά για την περίπτωση VictoriaMetrics, όπου timeseries_id είναι μια τιμή 64-bit, όπου τα πρώτα 32 bit είναι βασικά σταθερά και μόνο τα τελευταία 32 bit αλλάζουν.

Αυτή η δομή δεδομένων δεν αποθηκεύεται στο δίσκο, λειτουργεί μόνο στη μνήμη.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Εδώ είναι το API του. Δεν είναι πολύ περίπλοκο. Το API είναι προσαρμοσμένο ειδικά σε ένα συγκεκριμένο παράδειγμα χρήσης VictoriaMetrics. Δηλαδή, δεν υπάρχουν περιττές λειτουργίες εδώ. Εδώ είναι οι συναρτήσεις που χρησιμοποιούνται ρητά από τη VictoriaMetrics.

Υπάρχουν λειτουργίες add, που προσθέτει νέες αξίες. Υπάρχει μια λειτουργία has, το οποίο ελέγχει για νέες τιμές. Και υπάρχει μια λειτουργία del, το οποίο αφαιρεί τιμές. Υπάρχει μια βοηθητική λειτουργία len, το οποίο επιστρέφει το μέγεθος του σετ. Λειτουργία clone κλωνοποιεί πολύ. Και λειτουργία appendto μετατρέπει αυτό το σύνολο σε slice timeseries_ids.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Έτσι φαίνεται η υλοποίηση αυτής της δομής δεδομένων. Το σετ έχει δύο στοιχεία:

  • ItemsCount είναι ένα βοηθητικό πεδίο για γρήγορη επιστροφή του αριθμού των στοιχείων σε ένα σύνολο. Θα ήταν δυνατό να γίνει χωρίς αυτό το βοηθητικό πεδίο, αλλά έπρεπε να προστεθεί εδώ επειδή η VictoriaMetrics συχνά ρωτά το μήκος του σετ bits στους αλγόριθμούς της.

  • Το δεύτερο πεδίο είναι buckets. Αυτό είναι ένα κομμάτι από τη δομή bucket32. Κάθε δομή αποθηκεύει hi πεδίο. Αυτά είναι τα ανώτερα 32 bit. Και δύο φέτες - b16his и buckets του bucket16 δομές.

Τα κορυφαία 16 bit του δεύτερου μέρους της δομής των 64 bit αποθηκεύονται εδώ. Και εδώ τα bits αποθηκεύονται για τα χαμηλότερα 16 bit κάθε byte.

Bucket64 αποτελείται από έναν πίνακα uint64. Το μήκος υπολογίζεται χρησιμοποιώντας αυτές τις σταθερές. Σε μια bucket16 το μέγιστο μπορεί να αποθηκευτεί 2^16=65536 κομμάτι. Εάν το διαιρέσετε με το 8, τότε είναι 8 kilobyte. Αν διαιρέσετε ξανά με το 8, είναι 1000 uint64 έννοια. Αυτό είναι Bucket16 – αυτή είναι η δομή μας των 8 kilobyte.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Ας δούμε πώς εφαρμόζεται μια από τις μεθόδους αυτής της δομής για την προσθήκη μιας νέας τιμής.

Όλα ξεκινούν με uint64 νοήματα. Υπολογίζουμε τα πάνω 32 bit, υπολογίζουμε τα κάτω 32 bit. Ας τα περάσουμε όλα buckets. Συγκρίνουμε τα κορυφαία 32 bit σε κάθε κάδο με την τιμή που προστίθεται. Και αν ταιριάζουν, τότε καλούμε τη συνάρτηση add στη δομή b32 buckets. Και προσθέστε τα χαμηλότερα 32 bit εκεί. Και αν επέστρεφε true, τότε αυτό σημαίνει ότι προσθέσαμε μια τέτοια αξία εκεί και δεν είχαμε τέτοια αξία. Αν επιστρέψει false, τότε μια τέτοια έννοια υπήρχε ήδη. Στη συνέχεια αυξάνουμε τον αριθμό των στοιχείων στη δομή.

Αν δεν έχουμε βρει αυτό που χρειάζεστε bucket με την απαιτούμενη hi-value, τότε καλούμε τη συνάρτηση addAlloc, το οποίο θα παράγει ένα νέο bucket, προσθέτοντάς το στη δομή του κάδου.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Αυτή είναι η υλοποίηση της λειτουργίας b32.add. Είναι παρόμοια με την προηγούμενη εφαρμογή. Υπολογίζουμε τα πιο σημαντικά 16 bit, τα λιγότερο σημαντικά 16 bit.

Στη συνέχεια περνάμε από όλα τα ανώτερα 16 bit. Βρίσκουμε σπίρτα. Και αν υπάρχει αντιστοιχία, καλούμε τη μέθοδο προσθήκης, την οποία θα εξετάσουμε στην επόμενη σελίδα bucket16.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Και εδώ είναι το χαμηλότερο επίπεδο, το οποίο πρέπει να βελτιστοποιηθεί όσο το δυνατόν περισσότερο. Υπολογίζουμε για uint64 τιμή id σε bit slice και επίσης bitmask. Αυτή είναι μια μάσκα για μια δεδομένη τιμή 64-bit, η οποία μπορεί να χρησιμοποιηθεί για να ελέγξετε την παρουσία αυτού του bit ή να το ορίσετε. Ελέγχουμε αν αυτό το bit έχει οριστεί και το ρυθμίζουμε και επιστρέφουμε την παρουσία. Αυτή είναι η υλοποίησή μας, η οποία μας επέτρεψε να επιταχύνουμε τη λειτουργία τεμνόμενων αναγνωριστικών χρονοσειρών κατά 10 φορές σε σύγκριση με τους συμβατικούς χάρτες.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Εκτός από αυτήν τη βελτιστοποίηση, η VictoriaMetrics έχει πολλές άλλες βελτιστοποιήσεις. Οι περισσότερες από αυτές τις βελτιστοποιήσεις προστέθηκαν για κάποιο λόγο, αλλά μετά το προφίλ του κώδικα στην παραγωγή.

Αυτός είναι ο κύριος κανόνας της βελτιστοποίησης - μην προσθέτετε βελτιστοποίηση υποθέτοντας ότι θα υπάρχει ένα σημείο συμφόρησης εδώ, γιατί μπορεί να αποδειχθεί ότι δεν θα υπάρχει συμφόρηση εκεί. Η βελτιστοποίηση συνήθως υποβαθμίζει την ποιότητα του κώδικα. Επομένως, αξίζει να βελτιστοποιηθεί μόνο μετά τη δημιουργία προφίλ και κατά προτίμηση στην παραγωγή, έτσι ώστε αυτά να είναι πραγματικά δεδομένα. Αν κάποιος ενδιαφέρεται, μπορεί να δει τον πηγαίο κώδικα της VictoriaMetrics και να εξερευνήσει άλλες βελτιστοποιήσεις που υπάρχουν.

Μετάβαση σε βελτιστοποιήσεις στο VictoriaMetrics. Alexander Valyalkin

Έχω μια ερώτηση για το bitset. Πολύ παρόμοια με την εφαρμογή διανυσματικών bool C++, βελτιστοποιημένο σύνολο bits. Πήρες την υλοποίηση από εκεί;

Όχι από εκεί. Κατά την εφαρμογή αυτού του σετ bits, με καθοδήγησε η γνώση της δομής αυτών των χρονοσειρών αναγνωριστικών, που χρησιμοποιούνται στο VictoriaMetrics. Και η δομή τους είναι τέτοια που τα ανώτερα 32 bit είναι βασικά σταθερά. Τα χαμηλότερα 32 bit υπόκεινται σε αλλαγές. Όσο χαμηλότερο είναι το bit, τόσο πιο συχνά μπορεί να αλλάζει. Επομένως, αυτή η υλοποίηση έχει βελτιστοποιηθεί ειδικά για αυτήν τη δομή δεδομένων. Η υλοποίηση της C++, από όσο γνωρίζω, είναι βελτιστοποιημένη για τη γενική περίπτωση. Εάν κάνετε βελτιστοποίηση για τη γενική περίπτωση, αυτό σημαίνει ότι δεν θα είναι η βέλτιστη για μια συγκεκριμένη περίπτωση.

Σας συμβουλεύω επίσης να παρακολουθήσετε την αναφορά του Alexey Milovid. Πριν από ένα μήνα περίπου μίλησε για βελτιστοποίηση στο ClickHouse για συγκεκριμένες εξειδικεύσεις. Απλώς λέει ότι στη γενική περίπτωση, μια εφαρμογή C++ ή κάποια άλλη εφαρμογή είναι προσαρμοσμένη για να λειτουργεί καλά κατά μέσο όρο σε ένα νοσοκομείο. Ενδέχεται να έχει χειρότερη απόδοση από μια εφαρμογή ειδικής γνώσης όπως η δική μας, όπου γνωρίζουμε ότι τα κορυφαία 32 bit είναι ως επί το πλείστον σταθερά.

Έχω μια δεύτερη ερώτηση. Ποια είναι η θεμελιώδης διαφορά από το InfluxDB;

Υπάρχουν πολλές θεμελιώδεις διαφορές. Όσον αφορά τις επιδόσεις και την κατανάλωση μνήμης, το InfluxDB στις δοκιμές δείχνει 10 φορές περισσότερη κατανάλωση μνήμης για χρονοσειρές υψηλής καρδιαλικότητας, όταν έχετε πολλές από αυτές, για παράδειγμα, εκατομμύρια. Για παράδειγμα, το VictoriaMetrics καταναλώνει 1 GB ανά εκατομμύριο ενεργές σειρές, ενώ το InfluxDB καταναλώνει 10 GB. Και αυτό είναι μεγάλη διαφορά.

Η δεύτερη θεμελιώδης διαφορά είναι ότι το InfluxDB έχει περίεργες γλώσσες ερωτημάτων - Flux και InfluxQL. Δεν είναι πολύ βολικά για εργασία με χρονοσειρές σε σύγκριση με PromQL, το οποίο υποστηρίζεται από τη VictoriaMetrics. Η PromQL είναι μια γλώσσα ερωτημάτων από τον Prometheus.

Και μια ακόμη διαφορά είναι ότι το InfluxDB έχει ένα ελαφρώς περίεργο μοντέλο δεδομένων, όπου κάθε γραμμή μπορεί να αποθηκεύσει πολλά πεδία με διαφορετικό σύνολο ετικετών. Αυτές οι γραμμές χωρίζονται περαιτέρω σε διάφορους πίνακες. Αυτές οι πρόσθετες επιπλοκές περιπλέκουν την επακόλουθη εργασία με αυτήν τη βάση δεδομένων. Είναι δύσκολο να το υποστηρίξεις και να το καταλάβεις.

Στη VictoriaMetrics όλα είναι πολύ πιο απλά. Εκεί, κάθε χρονοσειρά είναι ένα κλειδί-τιμή. Η τιμή είναι ένα σύνολο σημείων - (timestamp, value), και το κλειδί είναι το σετ label=value. Δεν υπάρχει διαχωρισμός μεταξύ πεδίων και μετρήσεων. Σας επιτρέπει να επιλέξετε οποιαδήποτε δεδομένα και στη συνέχεια να συνδυάσετε, να προσθέσετε, να αφαιρέσετε, να πολλαπλασιάσετε, να διαιρέσετε, σε αντίθεση με το InfluxDB όπου οι υπολογισμοί μεταξύ διαφορετικών σειρών εξακολουθούν να μην εφαρμόζονται από όσο γνωρίζω. Ακόμα κι αν υλοποιηθούν, είναι δύσκολο, πρέπει να γράψεις πολύ κώδικα.

Έχω μια διευκρινιστική ερώτηση. Κατάλαβα καλά ότι υπήρχε κάποιο είδος προβλήματος για το οποίο μίλησες, ότι αυτός ο ανεστραμμένος δείκτης δεν χωράει στη μνήμη, άρα υπάρχει κατάτμηση εκεί;

Πρώτα, έδειξα μια αφελή υλοποίηση ενός ανεστραμμένου ευρετηρίου σε έναν τυπικό χάρτη Go. Αυτή η υλοποίηση δεν είναι κατάλληλη για βάσεις δεδομένων, επειδή αυτό το ανεστραμμένο ευρετήριο δεν αποθηκεύεται στο δίσκο και η βάση δεδομένων πρέπει να αποθηκεύεται στο δίσκο, έτσι ώστε αυτά τα δεδομένα να παραμένουν διαθέσιμα κατά την επανεκκίνηση. Σε αυτήν την υλοποίηση, όταν κάνετε επανεκκίνηση της εφαρμογής, το ανεστραμμένο ευρετήριό σας θα εξαφανιστεί. Και θα χάσετε την πρόσβαση σε όλα τα δεδομένα επειδή δεν θα μπορείτε να τα βρείτε.

Γειά σου! Ευχαριστώ για την αναφορά! Με λένε Πάβελ. Είμαι από τα Wildberries. Έχω μερικές ερωτήσεις για εσάς. Ερώτηση ένα. Πιστεύετε ότι εάν είχατε επιλέξει μια διαφορετική αρχή κατά τη δημιουργία της αρχιτεκτονικής της εφαρμογής σας και διαχωρίζατε τα δεδομένα με την πάροδο του χρόνου, τότε ίσως θα μπορούσατε να διασταυρώσετε δεδομένα κατά την αναζήτηση, με βάση μόνο το γεγονός ότι ένα διαμέρισμα περιέχει δεδομένα για ένα χρονική περίοδο, δηλαδή σε ένα χρονικό διάστημα και δεν θα έπρεπε να ανησυχείτε για το γεγονός ότι τα κομμάτια σας είναι διάσπαρτα διαφορετικά; Ερώτηση νούμερο 2 - αφού εφαρμόζετε έναν παρόμοιο αλγόριθμο με bitset και οτιδήποτε άλλο, τότε ίσως δοκιμάσατε να χρησιμοποιήσετε οδηγίες επεξεργαστή; Ίσως έχετε δοκιμάσει τέτοιες βελτιστοποιήσεις;

Θα απαντήσω αμέσως στο δεύτερο. Δεν έχουμε φτάσει ακόμα σε αυτό το σημείο. Αλλά αν χρειαστεί, θα φτάσουμε εκεί. Και το πρώτο, ποια ήταν η ερώτηση;

Συζητήσαμε δύο σενάρια. Και είπαν ότι διάλεξαν το δεύτερο με πιο σύνθετη υλοποίηση. Και δεν προτίμησαν την πρώτη, όπου τα δεδομένα κατανέμονται χρονικά.

Ναί. Στην πρώτη περίπτωση, ο συνολικός όγκος του ευρετηρίου θα ήταν μεγαλύτερος, γιατί σε κάθε διαμέρισμα θα έπρεπε να αποθηκεύουμε διπλά δεδομένα για εκείνες τις χρονοσειρές που συνεχίζονται σε όλες αυτές τις κατατμήσεις. Και αν ο ρυθμός ανατροπής της χρονοσειράς σας είναι μικρός, δηλαδή χρησιμοποιούνται συνεχώς οι ίδιες σειρές, τότε στην πρώτη περίπτωση θα χάναμε πολύ περισσότερο χώρο στον δίσκο που καταλαμβάνεται σε σύγκριση με τη δεύτερη περίπτωση.

Και έτσι - ναι, ο διαχωρισμός χρόνου είναι μια καλή επιλογή. Ο Προμηθέας το χρησιμοποιεί. Όμως ο Προμηθέας έχει ένα άλλο μειονέκτημα. Κατά τη συγχώνευση αυτών των τμημάτων δεδομένων, πρέπει να διατηρεί στη μνήμη μετα-πληροφορίες για όλες τις ετικέτες και τις χρονοσειρές. Επομένως, εάν τα κομμάτια δεδομένων που συγχωνεύει είναι μεγάλα, τότε η κατανάλωση μνήμης αυξάνεται πολύ κατά τη συγχώνευση, σε αντίθεση με τη VictoriaMetrics. Κατά τη συγχώνευση, το VictoriaMetrics δεν καταναλώνει καθόλου μνήμη· καταναλώνονται μόνο μερικά kilobyte, ανεξάρτητα από το μέγεθος των συγχωνευμένων τμημάτων δεδομένων.

Ο αλγόριθμος που χρησιμοποιείτε χρησιμοποιεί μνήμη. Επισημαίνει ετικέτες χρονοσειρών που περιέχουν τιμές. Και με αυτόν τον τρόπο ελέγχετε για ζευγαρωμένη παρουσία σε έναν πίνακα δεδομένων και σε έναν άλλο. Και καταλαβαίνετε αν έγινε τομή ή όχι. Συνήθως, οι βάσεις δεδομένων εφαρμόζουν δρομείς και επαναλήπτες που αποθηκεύουν το τρέχον περιεχόμενό τους και διατρέχουν τα ταξινομημένα δεδομένα λόγω της απλής πολυπλοκότητας αυτών των λειτουργιών.

Γιατί δεν χρησιμοποιούμε δρομείς για τη διέλευση δεδομένων;

Ναι.

Αποθηκεύουμε ταξινομημένες σειρές στο LevelDB ή στο mergeset. Μπορούμε να μετακινήσουμε τον κέρσορα και να βρούμε τη διασταύρωση. Γιατί δεν το χρησιμοποιούμε; Γιατί είναι αργό. Επειδή οι δρομείς σημαίνουν ότι πρέπει να καλέσετε μια συνάρτηση για κάθε γραμμή. Μια κλήση συνάρτησης είναι 5 νανοδευτερόλεπτα. Και αν έχετε 100 γραμμές, τότε αποδεικνύεται ότι ξοδεύουμε μισό δευτερόλεπτο απλώς καλώντας τη συνάρτηση.

Υπάρχει κάτι τέτοιο, ναι. Και η τελευταία μου ερώτηση. Η ερώτηση μπορεί να ακούγεται λίγο περίεργη. Γιατί δεν είναι δυνατό να διαβάσετε όλα τα απαραίτητα συγκεντρωτικά στοιχεία τη στιγμή που φτάνουν τα δεδομένα και να τα αποθηκεύσετε στην απαιτούμενη μορφή; Γιατί να εξοικονομήσετε τεράστιους τόμους σε ορισμένα συστήματα όπως το VictoriaMetrics, το ClickHouse κ.λπ., και στη συνέχεια να αφιερώσετε πολύ χρόνο σε αυτά;

Θα δώσω ένα παράδειγμα για να γίνει πιο σαφές. Ας πούμε πώς λειτουργεί ένα μικρό παιχνίδι ταχύμετρο; Καταγράφει την απόσταση που έχετε διανύσει, προσθέτοντάς την συνεχώς σε μία τιμή και στη δεύτερη φορά. Και χωρίζει. Και παίρνει μέση ταχύτητα. Μπορείτε να κάνετε περίπου το ίδιο πράγμα. Προσθέστε όλα τα απαραίτητα στοιχεία εν κινήσει.

Εντάξει, καταλαβαίνω την ερώτηση. Το παράδειγμά σου έχει τη θέση του. Εάν γνωρίζετε τι συγκεντρωτικά στοιχεία χρειάζεστε, τότε αυτή είναι η καλύτερη εφαρμογή. Αλλά το πρόβλημα είναι ότι οι άνθρωποι αποθηκεύουν αυτές τις μετρήσεις, ορισμένα δεδομένα στο ClickHouse και δεν γνωρίζουν ακόμη πώς θα τις συγκεντρώνουν και θα τις φιλτράρουν στο μέλλον, επομένως πρέπει να αποθηκεύσουν όλα τα ακατέργαστα δεδομένα. Αλλά αν γνωρίζετε ότι πρέπει να υπολογίσετε κάτι κατά μέσο όρο, τότε γιατί να μην το υπολογίσετε αντί να αποθηκεύσετε μια δέσμη ακατέργαστων τιμών εκεί; Αλλά αυτό είναι μόνο εάν γνωρίζετε ακριβώς τι χρειάζεστε.

Παρεμπιπτόντως, οι βάσεις δεδομένων για την αποθήκευση χρονοσειρών υποστηρίζουν την καταμέτρηση αδρανών. Για παράδειγμα, ο Προμηθέας υποστηρίζει κανόνες εγγραφής. Δηλαδή, αυτό μπορεί να γίνει αν γνωρίζετε ποιες μονάδες θα χρειαστείτε. Η VictoriaMetrics δεν το έχει ακόμα, αλλά συνήθως προηγείται ο Prometheus, στον οποίο μπορείτε να το κάνετε στους κανόνες επανακωδικοποίησης.

Για παράδειγμα, στην προηγούμενη εργασία μου έπρεπε να μετρήσω τον αριθμό των συμβάντων σε ένα συρόμενο παράθυρο την τελευταία ώρα. Το πρόβλημα είναι ότι έπρεπε να κάνω μια προσαρμοσμένη υλοποίηση στο Go, δηλαδή μια υπηρεσία για την καταμέτρηση αυτού του πράγματος. Αυτή η υπηρεσία ήταν τελικά μη τετριμμένη, γιατί είναι δύσκολο να υπολογιστεί. Η υλοποίηση μπορεί να είναι απλή εάν χρειάζεται να μετράτε ορισμένα συγκεντρωτικά στοιχεία σε σταθερά χρονικά διαστήματα. Εάν θέλετε να μετρήσετε συμβάντα σε ένα συρόμενο παράθυρο, τότε δεν είναι τόσο απλό όσο φαίνεται. Νομίζω ότι αυτό δεν έχει εφαρμοστεί ακόμη στο ClickHouse ή σε βάσεις δεδομένων χρονοσειρών, επειδή είναι δύσκολο να εφαρμοστεί.

Και μια ακόμα ερώτηση. Απλώς μιλούσαμε για τον μέσο όρο και θυμήθηκα ότι κάποτε υπήρχε κάτι όπως ο γραφίτης με ένα υπόστρωμα άνθρακα. Και ήξερε πώς να λεπτύνει τα παλιά δεδομένα, δηλαδή να αφήνει έναν πόντο ανά λεπτό, έναν πόντο την ώρα κ.λπ. Κατ 'αρχήν, αυτό είναι πολύ βολικό εάν χρειαζόμαστε ακατέργαστα δεδομένα, σχετικά μιλώντας, για ένα μήνα, και οτιδήποτε άλλο μπορεί να αραιωθεί . Αλλά η Prometheus και η VictoriaMetrics δεν υποστηρίζουν αυτήν τη λειτουργία. Σχεδιάζεται να το στηρίξουμε; Εάν όχι, γιατί όχι;

Ευχαριστώ για την ερώτηση. Οι χρήστες μας κάνουν αυτή την ερώτηση περιοδικά. Ρωτάνε πότε θα προσθέσουμε υποστήριξη για μείωση δειγματοληψίας. Υπάρχουν πολλά προβλήματα εδώ. Πρώτον, κάθε χρήστης καταλαβαίνει downsampling κάτι διαφορετικό: κάποιος θέλει να πάρει οποιοδήποτε αυθαίρετο σημείο σε ένα δεδομένο διάστημα, κάποιος θέλει μέγιστες, ελάχιστες, μέσες τιμές. Εάν πολλά συστήματα γράφουν δεδομένα στη βάση δεδομένων σας, τότε δεν μπορείτε να τα συγκεντρώσετε όλα μαζί. Μπορεί κάθε σύστημα να απαιτεί διαφορετική αραίωση. Και αυτό είναι δύσκολο να εφαρμοστεί.

Και το δεύτερο πράγμα είναι ότι η VictoriaMetrics, όπως και το ClickHouse, είναι βελτιστοποιημένη για εργασία σε μεγάλες ποσότητες ακατέργαστων δεδομένων, ώστε να μπορεί να φτυαρίζει ένα δισεκατομμύριο γραμμές σε λιγότερο από ένα δευτερόλεπτο, εάν έχετε πολλούς πυρήνες στο σύστημά σας. Σάρωση σημείων χρονοσειρών στο VictoriaMetrics - 50 σημεία ανά δευτερόλεπτο ανά πυρήνα. Και αυτή η απόδοση κλιμακώνεται στους υπάρχοντες πυρήνες. Δηλαδή, αν έχετε 000 πυρήνες, για παράδειγμα, θα σαρώσετε ένα δισεκατομμύριο πόντους ανά δευτερόλεπτο. Και αυτή η ιδιότητα των VictoriaMetrics και ClickHouse μειώνει την ανάγκη για downsamling.

Ένα άλλο χαρακτηριστικό είναι ότι η VictoriaMetrics συμπιέζει αποτελεσματικά αυτά τα δεδομένα. Η συμπίεση κατά μέσο όρο στην παραγωγή είναι από 0,4 έως 0,8 byte ανά σημείο. Κάθε σημείο είναι μια χρονική σήμανση + τιμή. Και συμπιέζεται σε λιγότερο από ένα byte κατά μέσο όρο.

Σεργκέι. Εχω μία ερώτηση. Ποιο είναι το κβαντικό ελάχιστου χρόνου εγγραφής;

Ένα χιλιοστό του δευτερολέπτου. Πρόσφατα είχαμε μια συνομιλία με άλλους προγραμματιστές βάσεων δεδομένων χρονοσειρών. Ο ελάχιστος χρόνος τους είναι ένα δευτερόλεπτο. Και στο Graphite, για παράδειγμα, είναι επίσης ένα δευτερόλεπτο. Στο OpenTSDB είναι επίσης ένα δευτερόλεπτο. Το InfluxDB έχει ακρίβεια νανοδευτερόλεπτου. Στη VictoriaMetrics είναι ένα χιλιοστό του δευτερολέπτου, γιατί στον Προμηθέα είναι ένα χιλιοστό του δευτερολέπτου. Και η VictoriaMetrics αναπτύχθηκε αρχικά ως απομακρυσμένη αποθήκευση για τον Prometheus. Τώρα όμως μπορεί να αποθηκεύσει δεδομένα από άλλα συστήματα.

Το άτομο με το οποίο μίλησα λέει ότι έχει ακρίβεια δευτερολέπτου σε δευτερόλεπτο - αυτό του αρκεί γιατί εξαρτάται από τον τύπο των δεδομένων που αποθηκεύονται στη βάση δεδομένων χρονοσειρών. Εάν πρόκειται για δεδομένα DevOps ή δεδομένα από την υποδομή, όπου τα συλλέγετε σε διαστήματα των 30 δευτερολέπτων, ανά λεπτό, τότε η ακρίβεια δευτερολέπτου είναι αρκετή, δεν χρειάζεστε τίποτα λιγότερο. Και αν συλλέγετε αυτά τα δεδομένα από συστήματα συναλλαγών υψηλής συχνότητας, τότε χρειάζεστε ακρίβεια νανοδευτερόλεπτου.

Η ακρίβεια χιλιοστού του δευτερολέπτου στο VictoriaMetrics είναι επίσης κατάλληλη για την περίπτωση DevOps και μπορεί να είναι κατάλληλη για τις περισσότερες περιπτώσεις που ανέφερα στην αρχή της αναφοράς. Το μόνο πράγμα για το οποίο μπορεί να μην είναι κατάλληλο είναι τα συστήματα συναλλαγών υψηλής συχνότητας.

Ευχαριστώ! Και μια άλλη ερώτηση. Τι είναι η συμβατότητα στο PromQL;

Πλήρης συμβατότητα προς τα πίσω. Η VictoriaMetrics υποστηρίζει πλήρως το PromQL. Επιπλέον, προσθέτει πρόσθετη προηγμένη λειτουργικότητα στο PromQL, η οποία ονομάζεται MetricsQL. Γίνεται συζήτηση στο YouTube για αυτήν την εκτεταμένη λειτουργία. Μίλησα στο Monitoring Meetup την άνοιξη στην Αγία Πετρούπολη.

Κανάλι τηλεγραφήματος VictoriaMetrics.

Μόνο εγγεγραμμένοι χρήστες μπορούν να συμμετάσχουν στην έρευνα. Συνδεθείτε, Σας παρακαλούμε.

Τι σας εμποδίζει να μεταβείτε στη VictoriaMetrics ως μακροπρόθεσμη αποθήκευση για τον Prometheus; (Γράψτε στα σχόλια, θα το προσθέσω στη δημοσκόπηση))

  • 71,4%Δεν χρησιμοποιώ Prometheus5

  • 28,6%Δεν ήξερα για το VictoriaMetrics2

Ψήφισαν 7 χρήστες. 12 χρήστες απείχαν.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο