Βάση δεδομένων Messenger (μέρος 2): διαχωρισμός "για το κέρδος"

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

Βάση δεδομένων Messenger (μέρος 2): διαχωρισμός "για το κέρδος"
Γεγονός είναι ότι Καθώς αυξάνεται το μέγεθος του πίνακα, αυξάνεται και το «βάθος» των ευρετηρίων. - αν και λογαριθμικά. Αλλά με την πάροδο του χρόνου αυτό αναγκάζει τον διακομιστή να εκτελεί τις ίδιες εργασίες ανάγνωσης/εγγραφής επεξεργάζεται πολλές φορές περισσότερες σελίδες δεδομένωνπαρά στην αρχή.

Εδώ είναι που έρχεται στη διάσωση τομή.

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

Δεν θα εξετάσουμε συγκεκριμένα σενάρια για την υλοποίηση διαμερισμάτων "στο υλικό", αλλά την ίδια την προσέγγιση - τι και πώς πρέπει να "κοπεί σε φέτες" και σε τι οδηγεί μια τέτοια επιθυμία.

Εννοια

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

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

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

Από αυτούς τους περιορισμούς είναι σαφές ότι η βέλτιστη λύση μηνύματος θα ήταν «καθημερινές» ενότητες - εξάλλου, ο χρήστης μας θα διαβάζει σχεδόν πάντα αυτό που του ήρθε "σήμερα" ή "χθες".

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

βήμα-βήμα

Γενικά, όλα όσα αναφέρθηκαν παραπάνω ακούγονται σαν ένα συνεχές κέρδος. Και είναι εφικτό, αλλά για αυτό θα πρέπει να προσπαθήσουμε σκληρά - γιατί η απόφαση για κατάτμηση μιας από τις οντότητες οδηγεί στην ανάγκη να «βλέπουμε» τη σχετική.

Το μήνυμα, οι ιδιότητες και οι προβολές του

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

Δεδομένου ότι μία από τις τυπικές μας εργασίες είναι η ακριβής προβολή καταχωρητών μηνυμάτων (μη αναγνωσμένα, εισερχόμενα, όλα), είναι επίσης λογικό να τα «ανασύρουμε» σε διαμερισμό κατά ημερομηνίες μηνυμάτων.

Βάση δεδομένων Messenger (μέρος 2): διαχωρισμός "για το κέρδος"

Προσθέτουμε το κλειδί κατάτμησης (ημερομηνία μηνύματος) σε όλους τους πίνακες: παραλήπτες, αρχείο, μητρώα. Δεν χρειάζεται να το προσθέσετε στο ίδιο το μήνυμα, αλλά χρησιμοποιήστε την υπάρχουσα DateTime.

Θέματα

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

Βάση δεδομένων Messenger (μέρος 2): διαχωρισμός "για το κέρδος"

Προσθέστε το κλειδί κατάτμησης (ημερομηνία θέματος) σε όλους τους πίνακες: θέμα, συμμετέχων.

Τώρα όμως έχουμε δύο προβλήματα ταυτόχρονα:

  • Σε ποια ενότητα πρέπει να αναζητήσω μηνύματα σχετικά με το θέμα;
  • Σε ποια ενότητα πρέπει να αναζητήσω το θέμα από το μήνυμα;

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

  • θα προσθέσουμε στο μήνυμα πεδίο ημερομηνίας θέματος
  • ας προσθέσουμε στο θέμα ορίστηκε ημερομηνία μηνύματος αυτή η αντιστοιχία (μπορεί να είναι ένας ξεχωριστός πίνακας ή μια σειρά ημερομηνιών)

Βάση δεδομένων Messenger (μέρος 2): διαχωρισμός "για το κέρδος"

Δεδομένου ότι θα υπάρξουν λίγες τροποποιήσεις στη λίστα ημερομηνιών μηνυμάτων για κάθε μεμονωμένη αλληλογραφία (εξάλλου, σχεδόν όλα τα μηνύματα πέφτουν σε 1-2 γειτονικές ημέρες), θα εστιάσω σε αυτήν την επιλογή.

Συνολικά, η δομή της βάσης δεδομένων μας πήρε την ακόλουθη μορφή, λαμβάνοντας υπόψη την κατάτμηση:

Πίνακες: RU, αν έχετε αποστροφή για το κυριλλικό αλφάβητο στα ονόματα των πινάκων/πεδίων, καλύτερα να μην κοιτάξετε

-- секции по дате сообщения
CREATE TABLE "Сообщение_YYYYMMDD"(
  "Сообщение"
    uuid
      PRIMARY KEY
, "Тема"
    uuid
, "ДатаТемы"
    date
, "Автор"
    uuid
, "ДатаВремя" -- используем как дату
    timestamp
, "Текст"
    text
);

CREATE TABLE "Адресат_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Сообщение"
    uuid
, "Персона"
    uuid
, PRIMARY KEY("Сообщение", "Персона")
);

CREATE TABLE "Файл_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Файл"
    uuid
      PRIMARY KEY
, "Сообщение"
    uuid
, "BLOB"
    uuid
, "Имя"
    text
);

CREATE TABLE "РеестрСообщений_YYYYMMDD"(
  "ДатаСообщения"
    date
, "Владелец"
    uuid
, "ТипРеестра"
    smallint
, "ДатаВремя"
    timestamp
, "Сообщение"
    uuid
, PRIMARY KEY("Владелец", "ТипРеестра", "Сообщение")
);
CREATE INDEX ON "РеестрСообщений_YYYYMMDD"("Владелец", "ТипРеестра", "ДатаВремя" DESC);

-- секции по дате темы
CREATE TABLE "Тема_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
      PRIMARY KEY
, "Документ"
    uuid
, "Название"
    text
);

CREATE TABLE "УчастникТемы_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
, "Персона"
    uuid
, PRIMARY KEY("Тема", "Персона")
);

CREATE TABLE "ДатыСообщенийТемы_YYYYMMDD"(
  "ДатаТемы"
    date
, "Тема"
    uuid
      PRIMARY KEY
, "Дата"
    date
);

Εξοικονομήστε μια όμορφη δεκάρα

Λοιπόν, τι γίνεται αν δεν χρησιμοποιούμε κλασική επιλογή τομής με βάση την κατανομή των τιμών των πεδίων (μέσω ενεργοποιήσεων και κληρονομικότητας ή PARTITION BY) και "μη αυτόματα" σε επίπεδο εφαρμογής, θα παρατηρήσετε ότι η τιμή του κλειδιού διαμερίσματος είναι ήδη αποθηκευμένη στο όνομα του ίδιου του πίνακα.

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

Πηγή: www.habr.com

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