Χαρακτηριστικά σχεδιασμού ενός μοντέλου δεδομένων για NoSQL

Εισαγωγή

Χαρακτηριστικά σχεδιασμού ενός μοντέλου δεδομένων για NoSQL «Πρέπει να τρέχεις όσο πιο γρήγορα μπορείς για να μείνεις στη θέση σου,
και για να φτάσεις κάπου, πρέπει να τρέξεις τουλάχιστον δύο φορές πιο γρήγορα!».
(γ) Η Αλίκη στη Χώρα των Θαυμάτων

Πριν από λίγο καιρό μου ζήτησαν να κάνω μια διάλεξη αναλυτές η εταιρεία μας στο θέμα του σχεδιασμού μοντέλων δεδομένων, επειδή καθισμένοι σε έργα για μεγάλο χρονικό διάστημα (μερικές φορές για αρκετά χρόνια) χάνουμε τα μάτια μας τι συμβαίνει γύρω μας στον κόσμο των τεχνολογιών πληροφορικής. Στην εταιρεία μας (συμβαίνει) πολλά έργα δεν χρησιμοποιούν βάσεις δεδομένων NoSQL (τουλάχιστον προς το παρόν), οπότε στη διάλεξή μου έδωσε ιδιαίτερη προσοχή σε αυτά χρησιμοποιώντας το παράδειγμα του HBase και προσπάθησα να προσανατολίσω την παρουσίαση του υλικού σε αυτά που δεν τα έχουν χρησιμοποιήσει ποτέ έχουν δουλέψει. Συγκεκριμένα, απεικόνισα μερικά από τα χαρακτηριστικά του σχεδιασμού μοντέλων δεδομένων χρησιμοποιώντας ένα παράδειγμα που διάβασα πριν από αρκετά χρόνια στο άρθρο «Introduction to HB ase Schema Design» του Amandeep Khurana. Κατά την ανάλυση παραδειγμάτων, συνέκρινα διάφορες επιλογές για την επίλυση του ίδιου προβλήματος, προκειμένου να μεταφέρω καλύτερα τις κύριες ιδέες στο κοινό.

Πρόσφατα, «από τίποτε να κάνω», έθεσα στον εαυτό μου την ερώτηση (το μακρύ Σαββατοκύριακο του Μαΐου στην καραντίνα είναι ιδιαίτερα ευνοϊκό για αυτό), πόσο θα αντιστοιχούν οι θεωρητικοί υπολογισμοί στην πράξη; Στην πραγματικότητα, έτσι γεννήθηκε η ιδέα για αυτό το άρθρο. Ένας προγραμματιστής που εργάζεται με NoSQL για αρκετές ημέρες μπορεί να μην μάθει τίποτα νέο από αυτό (και επομένως μπορεί να παραλείψει αμέσως το μισό άρθρο). Αλλά αναλυτέςΓια όσους δεν έχουν ακόμη συνεργαστεί στενά με το NoSQL, νομίζω ότι θα είναι χρήσιμο για να αποκτήσουν μια βασική κατανόηση των χαρακτηριστικών του σχεδιασμού μοντέλων δεδομένων για το HBase.

Παράδειγμα ανάλυσης

Κατά τη γνώμη μου, προτού αρχίσετε να χρησιμοποιείτε βάσεις δεδομένων NoSQL, πρέπει να σκεφτείτε προσεκτικά και να σταθμίσετε τα υπέρ και τα κατά. Συχνά το πρόβλημα μπορεί πιθανότατα να λυθεί χρησιμοποιώντας παραδοσιακά σχεσιακά DBMS. Επομένως, είναι καλύτερο να μην χρησιμοποιείτε NoSQL χωρίς σημαντικούς λόγους. Εάν παρόλα αυτά αποφασίσατε να χρησιμοποιήσετε μια βάση δεδομένων NoSQL, τότε θα πρέπει να λάβετε υπόψη ότι οι προσεγγίσεις σχεδιασμού εδώ είναι κάπως διαφορετικές. Ειδικά μερικά από αυτά μπορεί να είναι ασυνήθιστα για όσους έχουν ασχοληθεί στο παρελθόν μόνο με σχεσιακά DBMS (σύμφωνα με τις παρατηρήσεις μου). Έτσι, στον «σχεσιακό» κόσμο, συνήθως ξεκινάμε μοντελοποιώντας τον τομέα του προβλήματος και μόνο τότε, εάν είναι απαραίτητο, αποκανονικοποιούμε το μοντέλο. Στο NOSQL εμείς θα πρέπει να λάβει άμεσα υπόψη τα αναμενόμενα σενάρια για την εργασία με δεδομένα και αρχικά αποκανονικοποιήστε τα δεδομένα. Επιπλέον, υπάρχουν πολλές άλλες διαφορές, οι οποίες θα συζητηθούν παρακάτω.

Ας εξετάσουμε το ακόλουθο «συνθετικό» πρόβλημα, με το οποίο θα συνεχίσουμε να εργαζόμαστε:

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

  • Απαντήστε στην ερώτηση εάν ο χρήστης Α διαβάζει τον χρήστη Β (μοτίβο ανάγνωσης)
  • Να επιτρέπεται η προσθήκη/αφαίρεση συνδέσεων σε περίπτωση εγγραφής/απεγγραφής του χρήστη Α από τον χρήστη Β (πρότυπο αλλαγής δεδομένων)

Φυσικά, υπάρχουν πολλές επιλογές για την επίλυση του προβλήματος. Σε μια κανονική σχεσιακή βάση δεδομένων, πιθανότατα θα φτιάχναμε απλώς έναν πίνακα σχέσεων (ενδεχομένως τυποποιημένος εάν, για παράδειγμα, χρειάζεται να αποθηκεύσουμε μια ομάδα χρηστών: οικογένεια, εργασία κ.λπ., η οποία περιλαμβάνει αυτόν τον "φίλο") και να βελτιστοποιήσουμε Η ταχύτητα πρόσβασης θα προσθέσει ευρετήρια/διαμερίσματα. Πιθανότατα το τελικό τραπέζι θα μοιάζει κάπως έτσι:

user_id
friend_id

Vasya
Πέτια

Vasya
Όλια

Στο εξής, για λόγους σαφήνειας και καλύτερης κατανόησης, θα αναφέρω ονόματα αντί για ταυτότητες

Στην περίπτωση του HBase, γνωρίζουμε ότι:

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

Ως εκ τούτου, είμαστε αναγκασμένοι να χρησιμοποιήσουμε το αναγνωριστικό χρήστη ως κλειδί. Και η πρώτη μου σκέψη σχετικά με το θέμα "πού και πώς να αποθηκεύσω τις ταυτότητες φίλων;" ίσως μια ιδέα να τα αποθηκεύσετε σε στήλες. Αυτή η πιο προφανής και "αφελής" επιλογή θα μοιάζει κάπως έτσι (ας την ονομάσουμε Επιλογή 1 (προεπιλογή)για περαιτέρω αναφορά):

Κτύπος
ηχεία

Vasya
1: Πέτυα
2: Olya
3: Dasha

Πέτια
1: Μάσα
2: Vasya

Εδώ, κάθε γραμμή αντιστοιχεί σε έναν χρήστη δικτύου. Οι στήλες έχουν ονόματα: 1, 2, ... - ανάλογα με τον αριθμό των φίλων και τα αναγνωριστικά των φίλων αποθηκεύονται στις στήλες. Είναι σημαντικό να σημειωθεί ότι κάθε σειρά θα έχει διαφορετικό αριθμό στηλών. Στο παράδειγμα στο παραπάνω σχήμα, μια σειρά έχει τρεις στήλες (1, 2 και 3) και η δεύτερη έχει μόνο δύο (1 και 2) - εδώ εμείς οι ίδιοι χρησιμοποιήσαμε δύο ιδιότητες HBase που δεν έχουν οι σχεσιακές βάσεις δεδομένων:

  • τη δυνατότητα δυναμικής αλλαγής της σύνθεσης των στηλών (προσθήκη φίλου -> προσθήκη στήλης, αφαίρεση φίλου -> διαγραφή στήλης)
  • διαφορετικές σειρές μπορεί να έχουν διαφορετικές συνθέσεις στηλών

Ας ελέγξουμε τη δομή μας για συμμόρφωση με τις απαιτήσεις της εργασίας:

  • Ανάγνωση δεδομένων: για να καταλάβουμε αν ο Vasya είναι εγγεγραμμένος στο Olya, θα χρειαστεί να αφαιρέσουμε όλη η γραμμή με το κλειδί RowKey = "Vasya" και ταξινομήστε τις τιμές της στήλης μέχρι να "συναντήσουμε" την Olya σε αυτές. Ή επαναλάβετε τις τιμές όλων των στηλών, "not meet" Olya και επιστρέψτε την απάντηση False.
  • Επεξεργασία δεδομένων: προσθήκη φίλου: για μια παρόμοια εργασία πρέπει επίσης να αφαιρέσουμε όλη η γραμμή χρησιμοποιώντας το κλειδί RowKey = "Vasya" για να υπολογίσει τον συνολικό αριθμό των φίλων του. Χρειαζόμαστε αυτόν τον συνολικό αριθμό φίλων για να καθορίσουμε τον αριθμό της στήλης στην οποία πρέπει να γράψουμε το αναγνωριστικό του νέου φίλου.
  • Αλλαγή δεδομένων: διαγραφή φίλου:
    • Πρέπει να αφαιρεθεί όλη η γραμμή με το κλειδί RowKey = "Vasya" και ταξινομήστε τις στήλες για να βρείτε αυτή στην οποία έχει καταγραφεί ο φίλος που θα διαγραφεί.
    • Στη συνέχεια, μετά τη διαγραφή ενός φίλου, πρέπει να "μετατοπίσουμε" όλα τα δεδομένα σε μία στήλη για να μην υπάρχουν "κενά" στην αρίθμησή τους.

Ας αξιολογήσουμε τώρα πόσο παραγωγικοί θα είναι αυτοί οι αλγόριθμοι, τους οποίους θα χρειαστεί να εφαρμόσουμε στην πλευρά της «εφαρμογής υπό όρους», χρησιμοποιώντας Ο-συμπύκνωση. Ας υποδηλώσουμε το μέγεθος του υποθετικού κοινωνικού μας δικτύου ως n. Τότε ο μέγιστος αριθμός φίλων που μπορεί να έχει ένας χρήστης είναι (n-1). Μπορούμε να παραμελήσουμε περαιτέρω αυτό το (-1) για τους σκοπούς μας, καθώς στο πλαίσιο της χρήσης των συμβόλων Ο είναι ασήμαντο.

  • Ανάγνωση δεδομένων: είναι απαραίτητο να αφαιρέσετε ολόκληρη τη γραμμή και να επαναλάβετε όλες τις στήλες της στο όριο. Αυτό σημαίνει ότι η ανώτερη εκτίμηση του κόστους θα είναι περίπου O(n)
  • Επεξεργασία δεδομένων: προσθήκη φίλου: για να προσδιορίσετε τον αριθμό των φίλων, πρέπει να επαναλάβετε όλες τις στήλες της σειράς και, στη συνέχεια, να εισαγάγετε μια νέα στήλη => O(n)
  • Αλλαγή δεδομένων: διαγραφή φίλου:
    • Παρόμοια με την προσθήκη - πρέπει να περάσετε από όλες τις στήλες στο όριο => O(n)
    • Αφού αφαιρέσουμε τις στήλες, πρέπει να τις «μετακινήσουμε». Εάν εφαρμόσετε αυτό το "head-on", τότε στο όριο θα χρειαστείτε έως (n-1) λειτουργίες. Αλλά εδώ και περαιτέρω στο πρακτικό μέρος θα χρησιμοποιήσουμε μια διαφορετική προσέγγιση, η οποία θα εφαρμόσει μια «ψευδο-μετατόπιση» για έναν σταθερό αριθμό λειτουργιών - δηλαδή, θα δαπανηθεί σταθερός χρόνος σε αυτήν, ανεξάρτητα από το n. Αυτός ο σταθερός χρόνος (O(2) για την ακρίβεια) μπορεί να αγνοηθεί σε σύγκριση με το O(n). Η προσέγγιση απεικονίζεται στο παρακάτω σχήμα: απλώς αντιγράφουμε τα δεδομένα από την "τελευταία" στήλη σε αυτήν από την οποία θέλουμε να διαγράψουμε δεδομένα και, στη συνέχεια, διαγράφουμε την τελευταία στήλη:
      Χαρακτηριστικά σχεδιασμού ενός μοντέλου δεδομένων για NoSQL

Συνολικά, σε όλα τα σενάρια λάβαμε μια ασυμπτωτική υπολογιστική πολυπλοκότητα O(n).
Πιθανότατα έχετε ήδη παρατηρήσει ότι σχεδόν πάντα πρέπει να διαβάσουμε ολόκληρη τη σειρά από τη βάση δεδομένων και σε δύο περιπτώσεις από τις τρεις, απλώς για να περάσουμε από όλες τις στήλες και να υπολογίσουμε τον συνολικό αριθμό φίλων. Ως εκ τούτου, ως προσπάθεια βελτιστοποίησης, μπορείτε να προσθέσετε μια στήλη "count", η οποία αποθηκεύει τον συνολικό αριθμό φίλων κάθε χρήστη του δικτύου. Σε αυτήν την περίπτωση, δεν μπορούμε να διαβάσουμε ολόκληρη τη σειρά για να υπολογίσουμε τον συνολικό αριθμό φίλων, αλλά να διαβάσουμε μόνο μία στήλη "count". Το κύριο πράγμα δεν είναι να ξεχάσετε να ενημερώσετε το "count" κατά τον χειρισμό δεδομένων. Οτι. βελτιωνόμαστε Επιλογή 2 (μέτρηση):

Κτύπος
ηχεία

Vasya
1: Πέτυα
2: Olya
3: Dasha
καταμέτρηση: 3

Πέτια
1: Μάσα
2: Vasya

καταμέτρηση: 2

Σε σύγκριση με την πρώτη επιλογή:

  • Ανάγνωση δεδομένων: για να λάβετε απάντηση στην ερώτηση "Η Βάσια διαβάζει την Olya;" τίποτα δεν έχει αλλάξει => O(n)
  • Επεξεργασία δεδομένων: προσθήκη φίλου: Απλοποιήσαμε την εισαγωγή ενός νέου φίλου, αφού τώρα δεν χρειάζεται να διαβάσουμε ολόκληρη τη γραμμή και να επαναλάβουμε τις στήλες της, αλλά μπορούμε να πάρουμε μόνο την τιμή της στήλης "count" κ.λπ. προσδιορίστε αμέσως τον αριθμό της στήλης για να εισαγάγετε έναν νέο φίλο. Αυτό οδηγεί σε μείωση της υπολογιστικής πολυπλοκότητας σε O(1)
  • Αλλαγή δεδομένων: διαγραφή φίλου: Κατά τη διαγραφή ενός φίλου, μπορούμε επίσης να χρησιμοποιήσουμε αυτήν τη στήλη για να μειώσουμε τον αριθμό των λειτουργιών εισόδου/εξόδου όταν «μετακινούμε» τα δεδομένα ένα κελί προς τα αριστερά. Αλλά η ανάγκη επανάληψης μέσα από τις στήλες για να βρεθεί αυτή που πρέπει να διαγραφεί παραμένει, επομένως =>  O(n)
  • Από την άλλη πλευρά, τώρα κατά την ενημέρωση δεδομένων πρέπει να ενημερώνουμε τη στήλη "count" κάθε φορά, αλλά αυτό απαιτεί σταθερό χρόνο, ο οποίος μπορεί να παραμεληθεί στο πλαίσιο των συμβόλων O

Γενικά, η επιλογή 2 φαίνεται λίγο πιο βέλτιστη, αλλά μοιάζει περισσότερο με «εξέλιξη αντί για επανάσταση». Για να κάνουμε μια «επανάσταση» θα χρειαστούμε Επιλογή 3 (col).
Ας τα ανατρέψουμε όλα: θα διορίσουμε όνομα στήλης ID χρήστη! Αυτό που θα γραφτεί στην ίδια τη στήλη δεν είναι πλέον σημαντικό για εμάς, ας είναι ο αριθμός 1 (γενικά, χρήσιμα πράγματα μπορούν να αποθηκευτούν εκεί, για παράδειγμα, η ομάδα "οικογένεια/φίλοι/κ.λπ."). Αυτή η προσέγγιση μπορεί να εκπλήξει τον απροετοίμαστο «λαϊκό» που δεν έχει προηγούμενη εμπειρία εργασίας με βάσεις δεδομένων NoSQL, αλλά αυτή ακριβώς η προσέγγιση σας επιτρέπει να χρησιμοποιήσετε τις δυνατότητες του HBase σε αυτήν την εργασία πολύ πιο αποτελεσματικά:

Κτύπος
ηχεία

Vasya
Πέτια: 1
Olya: 1
Ντάσα: 1

Πέτια
Μάσα: 1
Βάσια: 1

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

  • Ανάγνωση δεδομένων: για να απαντήσετε στην ερώτηση εάν η Vasya είναι εγγεγραμμένη στο Olya, αρκεί να διαβάσετε μια στήλη "Olya": αν είναι εκεί, τότε η απάντηση είναι Σωστό, αν όχι - Λάθος => O(1)
  • Επεξεργασία δεδομένων: προσθήκη φίλου: Προσθήκη φίλου: απλώς προσθέστε μια νέα στήλη "Friend ID" => O(1)
  • Αλλαγή δεδομένων: διαγραφή φίλου: απλώς αφαιρέστε τη στήλη Αναγνωριστικό φίλου => O(1)

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

Μπορείτε να μπερδευτείτε και να προχωρήσετε λίγο πιο πέρα ​​στη διαδρομή της βελτιστοποίησης της απόδοσης και της μείωσης των λειτουργιών I/O κατά την πρόσβαση στη βάση δεδομένων. Τι θα γινόταν αν αποθηκεύαμε τις πλήρεις πληροφορίες σχέσης απευθείας στο ίδιο το κλειδί σειράς; Δηλαδή, να γίνει το κλειδί σύνθετο όπως userID.friendID; Σε αυτήν την περίπτωση, δεν χρειάζεται καν να διαβάσουμε καθόλου τις στήλες της γραμμής (Επιλογή 4 (σειρά)):

Κτύπος
ηχεία

Vasya.Petya
Πέτια: 1

Vasya.olya
Olya: 1

Βάσια.Ντάσα
Ντάσα: 1

Petya.masha
Μάσα: 1

Petya.Vasya
Βάσια: 1

Προφανώς, η αξιολόγηση όλων των σεναρίων χειρισμού δεδομένων σε μια τέτοια δομή, όπως στην προηγούμενη έκδοση, θα είναι O(1). Η διαφορά με την επιλογή 3 θα είναι αποκλειστικά στην αποτελεσματικότητα των λειτουργιών I/O στη βάση δεδομένων.

Λοιπόν, το τελευταίο "τόξο". Είναι εύκολο να δει κανείς ότι στην επιλογή 4, το κλειδί σειράς θα έχει μεταβλητό μήκος, το οποίο ενδέχεται να επηρεάσει την απόδοση (εδώ θυμόμαστε ότι το HBase αποθηκεύει δεδομένα ως σύνολο byte και οι σειρές στους πίνακες ταξινομούνται κατά κλειδί). Επιπλέον, έχουμε έναν διαχωριστή που μπορεί να χρειαστεί να αντιμετωπιστεί σε ορισμένα σενάρια. Για να εξαλείψετε αυτήν την επιρροή, μπορείτε να χρησιμοποιήσετε κατακερματισμούς από το userID και το friendID, και επειδή και οι δύο κατακερματισμοί θα έχουν σταθερό μήκος, μπορείτε απλά να τους συνδέσετε, χωρίς διαχωριστικό. Τότε τα δεδομένα στον πίνακα θα μοιάζουν με αυτό (Επιλογή 5 (hash)):

Κτύπος
ηχεία

dc084ef00e94aef49be885f9b01f51c01918fa783851db0dc1f72f83d33a5994
Πέτια: 1

dc084ef00e94aef49be885f9b01f51c0f06b7714b5ba522c3cf51328b66fe28a
Olya: 1

dc084ef00e94aef49be885f9b01f51c00d2c2e5d69df6b238754f650d56c896a
Ντάσα: 1

1918fa783851db0dc1f72f83d33a59949ee3309645bd2c0775899fca14f311e1
Μάσα: 1

1918fa783851db0dc1f72f83d33a5994dc084ef00e94aef49be885f9b01f51c0
Βάσια: 1

Προφανώς, η αλγοριθμική πολυπλοκότητα της εργασίας με μια τέτοια δομή στα σενάρια που εξετάζουμε θα είναι ίδια με αυτή της επιλογής 4 - δηλαδή O(1).
Συνολικά, ας συνοψίσουμε όλες τις εκτιμήσεις μας για την υπολογιστική πολυπλοκότητα σε έναν πίνακα:

Προσθήκη φίλου
Έλεγχος σε έναν φίλο
Αφαίρεση φίλου

Επιλογή 1 (προεπιλογή)
O (n)
O (n)
O (n)

Επιλογή 2 (μέτρηση)
Ο (1)
O (n)
O (n)

Επιλογή 3 (στήλη)
Ο (1)
Ο (1)
Ο (1)

Επιλογή 4 (σειρά)
Ο (1)
Ο (1)
Ο (1)

Επιλογή 5 (κατακερματισμός)
Ο (1)
Ο (1)
Ο (1)

Όπως μπορείτε να δείτε, οι επιλογές 3-5 φαίνεται να είναι οι πιο προτιμητέες και θεωρητικά διασφαλίζουν την εκτέλεση όλων των απαραίτητων σεναρίων χειρισμού δεδομένων σε σταθερό χρόνο. Υπό τις συνθήκες της εργασίας μας, δεν υπάρχει ρητή απαίτηση να αποκτήσουμε μια λίστα με όλους τους φίλους του χρήστη, αλλά σε πραγματικές δραστηριότητες έργου, θα ήταν καλό για εμάς, ως καλοί αναλυτές, να «προβλέπουμε» ότι μπορεί να προκύψει μια τέτοια εργασία και «Άπλωσε ένα καλαμάκι». Ως εκ τούτου, η συμπάθειά μου είναι με την πλευρά της επιλογής 3. Αλλά είναι πολύ πιθανό ότι σε ένα πραγματικό έργο αυτό το αίτημα θα μπορούσε να έχει ήδη λυθεί με άλλα μέσα, επομένως, χωρίς μια γενική εικόνα του όλου προβλήματος, είναι καλύτερα να μην κάνουμε τελικά συμπεράσματα.

Προετοιμασία του πειράματος

Θα ήθελα να δοκιμάσω τα παραπάνω θεωρητικά επιχειρήματα στην πράξη - αυτός ήταν ο στόχος της ιδέας που προέκυψε το μεγάλο Σαββατοκύριακο. Για να γίνει αυτό, είναι απαραίτητο να αξιολογήσουμε την ταχύτητα λειτουργίας της «εφαρμογής υπό όρους» σε όλα τα περιγραφόμενα σενάρια χρήσης της βάσης δεδομένων, καθώς και την αύξηση σε αυτόν τον χρόνο με την αύξηση του μεγέθους του κοινωνικού δικτύου (n). Η παράμετρος στόχος που μας ενδιαφέρει και την οποία θα μετρήσουμε κατά τη διάρκεια του πειράματος είναι ο χρόνος που αφιερώνει η «εφαρμογή υπό όρους» για την εκτέλεση μιας «επιχειρηματικής λειτουργίας». Με τον όρο «επαγγελματική συναλλαγή» εννοούμε ένα από τα ακόλουθα:

  • Προσθήκη ενός νέου φίλου
  • Έλεγχος εάν ο χρήστης Α είναι φίλος του χρήστη Β
  • Αφαίρεση ενός φίλου

Έτσι, λαμβάνοντας υπόψη τις απαιτήσεις που περιγράφονται στην αρχική δήλωση, το σενάριο επαλήθευσης προκύπτει ως εξής:

  • Καταγραφή δεδομένων. Δημιουργήστε τυχαία ένα αρχικό δίκτυο μεγέθους n. Για να φτάσουμε πιο κοντά στον «πραγματικό κόσμο», ο αριθμός των φίλων που έχει κάθε χρήστης είναι επίσης μια τυχαία μεταβλητή. Μετρήστε το χρόνο που χρειάζεται για να εγγράψει όλα τα δεδομένα που δημιουργούνται στο HBase η «αίτησή μας υπό όρους». Στη συνέχεια, διαιρέστε τον χρόνο που προκύπτει με τον συνολικό αριθμό των προστιθέμενων φίλων - έτσι παίρνουμε τον μέσο χρόνο για μια "επιχειρηματική λειτουργία"
  • Ανάγνωση δεδομένων. Για κάθε χρήστη, δημιουργήστε μια λίστα με «προσωπικότητες» για τις οποίες πρέπει να λάβετε απάντηση εάν ο χρήστης είναι εγγεγραμμένος σε αυτές ή όχι. Το μήκος της λίστας = περίπου ο αριθμός των φίλων του χρήστη, και για τους μισούς από τους επιλεγμένους φίλους η απάντηση θα πρέπει να είναι "Ναι" και για τους άλλους μισούς - "Όχι". Ο έλεγχος εκτελείται με τέτοια σειρά ώστε οι απαντήσεις "Ναι" και "Όχι" να εναλλάσσονται (δηλαδή, σε κάθε δεύτερη περίπτωση θα πρέπει να περάσουμε από όλες τις στήλες της γραμμής για τις επιλογές 1 και 2). Στη συνέχεια, ο συνολικός χρόνος προβολής διαιρείται με τον αριθμό των φίλων που δοκιμάστηκαν για να ληφθεί ο μέσος χρόνος προβολής ανά θέμα.
  • Διαγραφή δεδομένων. Καταργήστε όλους τους φίλους από τον χρήστη. Επιπλέον, η σειρά διαγραφής είναι τυχαία (δηλαδή «ανακατεύουμε» την αρχική λίστα που χρησιμοποιήθηκε για την καταγραφή των δεδομένων). Στη συνέχεια, ο συνολικός χρόνος ελέγχου διαιρείται με τον αριθμό των φίλων που αφαιρέθηκαν για να ληφθεί ο μέσος χρόνος ανά έλεγχο.

Τα σενάρια πρέπει να εκτελούνται για καθεμία από τις 5 επιλογές μοντέλων δεδομένων και για διαφορετικά μεγέθη του κοινωνικού δικτύου για να δείτε πώς αλλάζει ο χρόνος καθώς μεγαλώνει. Εντός ενός n, οι συνδέσεις στο δίκτυο και η λίστα των χρηστών προς έλεγχο πρέπει, φυσικά, να είναι ίδιες και για τις 5 επιλογές.
Για καλύτερη κατανόηση, παρακάτω είναι ένα παράδειγμα δημιουργημένων δεδομένων για n= 5. Η γραπτή "γεννήτρια" παράγει τρία λεξικά αναγνωριστικών ως έξοδο:

  • το πρώτο είναι για εισαγωγή
  • το δεύτερο είναι για έλεγχο
  • Τρίτον - για διαγραφή

{0: [1], 1: [4, 5, 3, 2, 1], 2: [1, 2], 3: [2, 4, 1, 5, 3], 4: [2, 1]} # всего 15 друзей

{0: [1, 10800], 1: [5, 10800, 2, 10801, 4, 10802], 2: [1, 10800], 3: [3, 10800, 1, 10801, 5, 10802], 4: [2, 10800]} # всего 18 проверяемых субъектов

{0: [1], 1: [1, 3, 2, 5, 4], 2: [1, 2], 3: [4, 1, 2, 3, 5], 4: [1, 2]} # всего 15 друзей

Όπως μπορείτε να δείτε, όλα τα αναγνωριστικά μεγαλύτερα από 10 στο λεξικό για έλεγχο είναι ακριβώς αυτά που σίγουρα θα δώσουν την απάντηση False. Η εισαγωγή, ο έλεγχος και η διαγραφή «φίλων» πραγματοποιείται ακριβώς με τη σειρά που καθορίζεται στο λεξικό.

Το πείραμα διεξήχθη σε φορητό υπολογιστή με Windows 10, όπου το HBase εκτελούσε το ένα κοντέινερ Docker και το Python με το Jupyter Notebook στο άλλο. Στο Docker διατέθηκαν 2 πυρήνες CPU και 2 GB μνήμης RAM. Όλη η λογική, τόσο η εξομοίωση της «εφαρμογής υπό όρους» όσο και η «σωλήνωση» για τη δημιουργία δεδομένων δοκιμής και τη μέτρηση του χρόνου, γράφτηκε σε Python. Η βιβλιοθήκη χρησιμοποιήθηκε για εργασία με το HBase happybase, για τον υπολογισμό των κατακερματισμών (MD5) για την επιλογή 5 - hashlib

Λαμβάνοντας υπόψη την υπολογιστική ισχύ ενός συγκεκριμένου φορητού υπολογιστή, επιλέχθηκε πειραματικά μια εκκίνηση για n = 10, 30, …. 170 – όταν ο συνολικός χρόνος λειτουργίας του πλήρους κύκλου δοκιμών (όλα τα σενάρια για όλες τις επιλογές για όλα τα n) ήταν ακόμη περισσότερο ή λιγότερο λογικός και κατάλληλος κατά τη διάρκεια ενός πάρτι τσαγιού (κατά μέσο όρο 15 λεπτά).

Εδώ είναι απαραίτητο να κάνουμε μια παρατήρηση ότι σε αυτό το πείραμα δεν αξιολογούμε πρωτίστως απόλυτους αριθμούς απόδοσης. Ακόμη και μια σχετική σύγκριση διαφορετικών δύο επιλογών μπορεί να μην είναι απολύτως σωστή. Τώρα μας ενδιαφέρει η φύση της αλλαγής του χρόνου ανάλογα με το n, αφού λαμβάνοντας υπόψη την παραπάνω διαμόρφωση του "test stand", είναι πολύ δύσκολο να λάβουμε εκτιμήσεις χρόνου "καθαρισμένες" από την επίδραση τυχαίων και άλλων παραγόντων ( και δεν τέθηκε τέτοιο καθήκον).

Το αποτέλεσμα του πειράματος

Η πρώτη δοκιμή είναι πώς αλλάζει ο χρόνος που δαπανάται για τη συμπλήρωση της λίστας φίλων. Το αποτέλεσμα είναι στο παρακάτω γράφημα.
Χαρακτηριστικά σχεδιασμού ενός μοντέλου δεδομένων για NoSQL
Οι επιλογές 3-5, όπως αναμενόταν, δείχνουν έναν σχεδόν σταθερό χρόνο «επιχειρηματικής συναλλαγής», ο οποίος δεν εξαρτάται από την αύξηση του μεγέθους του δικτύου και μια δυσδιάκριτη διαφορά στην απόδοση.
Η επιλογή 2 δείχνει επίσης σταθερή, αλλά ελαφρώς χειρότερη απόδοση, σχεδόν ακριβώς 2 φορές σε σχέση με τις επιλογές 3-5. Και αυτό δεν μπορεί παρά να χαρεί, αφού συσχετίζεται με τη θεωρία - σε αυτήν την έκδοση ο αριθμός των λειτουργιών εισόδου/εξόδου προς/από το HBase είναι ακριβώς 2 φορές μεγαλύτερος. Αυτό μπορεί να χρησιμεύσει ως έμμεση απόδειξη ότι ο πάγκος δοκιμών μας, κατ' αρχήν, παρέχει καλή ακρίβεια.
Η επιλογή 1 επίσης, όπως αναμενόταν, αποδεικνύεται ότι είναι η πιο αργή και δείχνει μια γραμμική αύξηση του χρόνου που αφιερώνεται για την προσθήκη μεταξύ τους στο μέγεθος του δικτύου.
Ας δούμε τώρα τα αποτελέσματα του δεύτερου τεστ.
Χαρακτηριστικά σχεδιασμού ενός μοντέλου δεδομένων για NoSQL
Οι επιλογές 3-5 συμπεριφέρονται και πάλι όπως αναμένεται - σταθερός χρόνος, ανεξάρτητα από το μέγεθος του δικτύου. Οι επιλογές 1 και 2 δείχνουν μια γραμμική αύξηση του χρόνου καθώς αυξάνεται το μέγεθος του δικτύου και παρόμοια απόδοση. Επιπλέον, η επιλογή 2 αποδεικνύεται λίγο πιο αργή - προφανώς λόγω της ανάγκης διόρθωσης και επεξεργασίας της πρόσθετης στήλης "count", η οποία γίνεται πιο αισθητή καθώς το n μεγαλώνει. Αλλά και πάλι θα αποφύγω να βγάλω συμπεράσματα, καθώς η ακρίβεια αυτής της σύγκρισης είναι σχετικά χαμηλή. Επιπλέον, αυτές οι αναλογίες (η οποία η επιλογή, 1 ή 2, είναι πιο γρήγορη) άλλαξαν από τρέξιμο σε τρέξιμο (διατηρώντας παράλληλα τη φύση της εξάρτησης και "τρέξιμο με λαιμό").

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

Χαρακτηριστικά σχεδιασμού ενός μοντέλου δεδομένων για NoSQL

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

Οι επιλογές 1 και 2, όπως αναμενόταν, δείχνουν μια γραμμική αύξηση του χρόνου. Ταυτόχρονα, η επιλογή 2 είναι σταθερά πιο αργή από την επιλογή 1 - λόγω της πρόσθετης λειτουργίας I/O για τη «διατήρηση» της στήλης μέτρησης.

Γενικά συμπεράσματα του πειράματος:

  • Οι επιλογές 3-5 δείχνουν μεγαλύτερη αποτελεσματικότητα καθώς εκμεταλλεύονται το HBase. Επιπλέον, η απόδοσή τους διαφέρει μεταξύ τους κατά σταθερά και δεν εξαρτάται από το μέγεθος του δικτύου.
  • Η διαφορά μεταξύ των επιλογών 4 και 5 δεν καταγράφηκε. Αυτό όμως δεν σημαίνει ότι η επιλογή 5 δεν πρέπει να χρησιμοποιηθεί. Είναι πιθανό ότι το πειραματικό σενάριο που χρησιμοποιήθηκε, λαμβάνοντας υπόψη τα χαρακτηριστικά απόδοσης του πάγκου δοκιμών, δεν επέτρεψε τον εντοπισμό του.
  • Η φύση της αύξησης του χρόνου που απαιτείται για την εκτέλεση «επιχειρηματικών πράξεων» με δεδομένα επιβεβαίωσε γενικά τους θεωρητικούς υπολογισμούς που ελήφθησαν προηγουμένως για όλες τις επιλογές.

Επίλογος

Τα πρόχειρα πειράματα που πραγματοποιήθηκαν δεν πρέπει να εκληφθούν ως απόλυτη αλήθεια. Υπάρχουν πολλοί παράγοντες που δεν ελήφθησαν υπόψη και παραμόρφωσαν τα αποτελέσματα (αυτές οι διακυμάνσεις είναι ιδιαίτερα ορατές στα γραφήματα με μικρό μέγεθος δικτύου). Για παράδειγμα, η ταχύτητα του thrift, που χρησιμοποιείται από το happybase, ο όγκος και η μέθοδος υλοποίησης της λογικής που έγραψα στην Python (δεν μπορώ να ισχυριστώ ότι ο κώδικας γράφτηκε βέλτιστα και χρησιμοποίησε αποτελεσματικά τις δυνατότητες όλων των στοιχείων), ίσως οι δυνατότητες της προσωρινής αποθήκευσης HBase, η δραστηριότητα παρασκηνίου των Windows 10 στον φορητό υπολογιστή μου κ.λπ. Σε γενικές γραμμές, μπορούμε να υποθέσουμε ότι όλοι οι θεωρητικοί υπολογισμοί έχουν αποδείξει πειραματικά την εγκυρότητά τους. Λοιπόν, ή τουλάχιστον δεν ήταν δυνατό να τους αντικρούσουμε με μια τέτοια «μετωπική επίθεση».

Συμπερασματικά, συστάσεις για όλους όσοι μόλις αρχίζουν να σχεδιάζουν μοντέλα δεδομένων στο HBase: αφηρημένη από προηγούμενη εμπειρία εργασίας με σχεσιακές βάσεις δεδομένων και θυμηθείτε τις «εντολές»:

  • Κατά το σχεδιασμό, προχωράμε από την εργασία και τα μοτίβα χειρισμού δεδομένων και όχι από το μοντέλο τομέα
  • Αποτελεσματική πρόσβαση (χωρίς πλήρη σάρωση πίνακα) – μόνο με κλειδί
  • Αποκανονικοποίηση
  • Διαφορετικές σειρές μπορεί να περιέχουν διαφορετικές στήλες
  • Δυναμική σύνθεση ηχείων

Πηγή: www.habr.com

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