Ανθεκτική αποθήκευση δεδομένων και API αρχείων Linux

Εγώ, ερευνώντας τη σταθερότητα της αποθήκευσης δεδομένων σε συστήματα cloud, αποφάσισα να δοκιμάσω τον εαυτό μου, για να βεβαιωθώ ότι καταλαβαίνω τα βασικά πράγματα. Εγώ ξεκίνησε διαβάζοντας την προδιαγραφή NVMe προκειμένου να κατανοήσουμε ποιες εγγυήσεις σχετικά με τη διατήρηση των δεδομένων (δηλαδή, εγγυήσεις ότι τα δεδομένα θα είναι διαθέσιμα μετά από μια αποτυχία συστήματος) μας δίνουν οι δίσκοι NMVe. Έκανα τα ακόλουθα κύρια συμπεράσματα: πρέπει να λάβετε υπόψη τα δεδομένα που έχουν καταστραφεί από τη στιγμή που δίνεται η εντολή εγγραφής δεδομένων και μέχρι τη στιγμή που θα εγγραφούν στο μέσο αποθήκευσης. Ωστόσο, στα περισσότερα προγράμματα, οι κλήσεις συστήματος χρησιμοποιούνται με ασφάλεια για την εγγραφή δεδομένων.

Σε αυτό το άρθρο, εξερευνώ τους μηχανισμούς επιμονής που παρέχονται από τα API αρχείων Linux. Φαίνεται ότι όλα θα πρέπει να είναι απλά εδώ: το πρόγραμμα καλεί την εντολή write(), και αφού ολοκληρωθεί η λειτουργία αυτής της εντολής, τα δεδομένα θα αποθηκευτούν με ασφάλεια στο δίσκο. Αλλά write() αντιγράφει μόνο τα δεδομένα της εφαρμογής στη μνήμη cache του πυρήνα που βρίσκεται στη μνήμη RAM. Για να αναγκαστεί το σύστημα να εγγράψει δεδομένα στο δίσκο, πρέπει να χρησιμοποιηθούν ορισμένοι πρόσθετοι μηχανισμοί.

Ανθεκτική αποθήκευση δεδομένων και API αρχείων Linux

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

Δυνατότητες χρήσης της συνάρτησης write().

Κλήση συστήματος write() ορίζεται στο πρότυπο IEEE POSIX ως προσπάθεια εγγραφής δεδομένων σε έναν περιγραφέα αρχείου. Μετά την επιτυχή ολοκλήρωση των εργασιών write() Οι λειτουργίες ανάγνωσης δεδομένων πρέπει να επιστρέφουν ακριβώς τα byte που είχαν γραφτεί προηγουμένως, ακόμα κι αν γίνεται πρόσβαση στα δεδομένα από άλλες διεργασίες ή νήματα (εδώ αντίστοιχη ενότητα του προτύπου POSIX). Εδώ, στην ενότητα για την αλληλεπίδραση των νημάτων με τις κανονικές λειτουργίες αρχείων, υπάρχει μια σημείωση που λέει ότι εάν δύο νήματα το καθένα καλεί αυτές τις συναρτήσεις, τότε κάθε κλήση πρέπει είτε να δει όλες τις υποδεικνυόμενες συνέπειες στις οποίες οδηγεί η εκτέλεση της άλλης κλήσης, ή δεν βλέπω καθόλου συνέπειες. Αυτό οδηγεί στο συμπέρασμα ότι όλες οι λειτουργίες εισόδου/εξόδου αρχείων πρέπει να κρατούν ένα κλείδωμα στον πόρο που εργάζεται.

Μήπως αυτό σημαίνει ότι η επέμβαση write() είναι ατομικό; Από τεχνικής άποψης, ναι. Οι λειτουργίες ανάγνωσης δεδομένων πρέπει να επιστρέψουν είτε όλα είτε κανένα από αυτά που γράφτηκαν write(). Αλλά η επέμβαση write(), σύμφωνα με το πρότυπο, δεν χρειάζεται να τελειώσει, έχοντας σημειώσει όλα όσα της ζητήθηκε να γράψει. Επιτρέπεται η εγγραφή μέρους μόνο των δεδομένων. Για παράδειγμα, μπορεί να έχουμε δύο ροές που προσθέτουν 1024 byte σε ένα αρχείο που περιγράφεται από τον ίδιο περιγραφέα αρχείου. Από την άποψη του προτύπου, το αποτέλεσμα θα είναι αποδεκτό όταν κάθε μία από τις λειτουργίες εγγραφής μπορεί να προσαρτήσει μόνο ένα byte στο αρχείο. Αυτές οι λειτουργίες θα παραμείνουν ατομικές, αλλά μετά την ολοκλήρωσή τους, τα δεδομένα που γράφουν στο αρχείο θα μπερδευτούν. Εδώ πολύ ενδιαφέρουσα συζήτηση για αυτό το θέμα στο Stack Overflow.

Συναρτήσεις fsync() και fdatasync().

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

Ένα πρόβλημα που μπορεί να προκύψει εδώ είναι ότι αυτοί οι μηχανισμοί δεν εγγυώνται ότι το αρχείο μπορεί να βρεθεί μετά από μια πιθανή αποτυχία. Συγκεκριμένα, όταν δημιουργείται ένα νέο αρχείο, θα πρέπει να καλέσετε fsync() για τον κατάλογο που το περιέχει. Διαφορετικά, μετά από ένα σφάλμα, μπορεί να αποδειχθεί ότι αυτό το αρχείο δεν υπάρχει. Ο λόγος για αυτό είναι ότι στο UNIX, λόγω της χρήσης σκληρών συνδέσμων, ένα αρχείο μπορεί να υπάρχει σε πολλούς καταλόγους. Επομένως, όταν καλείτε fsync() δεν υπάρχει τρόπος για ένα αρχείο να γνωρίζει ποια δεδομένα καταλόγου θα πρέπει επίσης να ξεπλυθούν στο δίσκο (εδώ μπορείτε να διαβάσετε περισσότερα για αυτό). Φαίνεται ότι το σύστημα αρχείων ext4 είναι ικανό αυτόματα ισχύουν fsync() σε καταλόγους που περιέχουν τα αντίστοιχα αρχεία, αλλά αυτό μπορεί να μην συμβαίνει με άλλα συστήματα αρχείων.

Αυτός ο μηχανισμός μπορεί να εφαρμοστεί διαφορετικά σε διαφορετικά συστήματα αρχείων. χρησιμοποίησα blktrace για να μάθετε ποιες λειτουργίες δίσκου χρησιμοποιούνται στα συστήματα αρχείων ext4 και XFS. Και οι δύο εκδίδουν τις συνήθεις εντολές εγγραφής στο δίσκο τόσο για τα περιεχόμενα των αρχείων όσο και για το ημερολόγιο του συστήματος αρχείων, ξεπλένουν τη μνήμη cache και βγαίνουν εκτελώντας μια εγγραφή FUA (Force Unit Access, εγγραφή δεδομένων απευθείας στο δίσκο, παράκαμψη της κρυφής μνήμης) στο ημερολόγιο. Μάλλον κάνουν ακριβώς αυτό για να επιβεβαιώσουν το γεγονός της συναλλαγής. Σε μονάδες που δεν υποστηρίζουν FUA, αυτό προκαλεί δύο εκροές της προσωρινής μνήμης. Τα πειράματά μου το έχουν δείξει fdatasync() λίγο πιο γρήγορα fsync(). Χρησιμότητα blktrace υποδηλώνει ότι fdatasync() συνήθως γράφει λιγότερα δεδομένα στο δίσκο (στο ext4 fsync() γράφει 20 KiB, και fdatasync() - 16 KiB). Επίσης, ανακάλυψα ότι το XFS είναι ελαφρώς πιο γρήγορο από το ext4. Και εδώ με τη βοήθεια blktrace μπόρεσε να το ανακαλύψει fdatasync() ξεπλένει λιγότερα δεδομένα στο δίσκο (4 KiB σε XFS).

Διφορούμενες καταστάσεις κατά τη χρήση της fsync()

Μπορώ να σκεφτώ τρεις διφορούμενες καταστάσεις που αφορούν fsync()που έχω συναντήσει στην πράξη.

Το πρώτο τέτοιο περιστατικό συνέβη το 2008. Εκείνη την εποχή, η διεπαφή του Firefox 3 «πάγωσε» εάν εγγραφόταν μεγάλος αριθμός αρχείων στο δίσκο. Το πρόβλημα ήταν ότι η υλοποίηση της διεπαφής χρησιμοποιούσε μια βάση δεδομένων SQLite για την αποθήκευση πληροφοριών σχετικά με την κατάστασή της. Μετά από κάθε αλλαγή που συνέβαινε στη διεπαφή, καλούνταν η συνάρτηση fsync(), το οποίο παρείχε καλές εγγυήσεις για σταθερή αποθήκευση δεδομένων. Στο τότε χρησιμοποιούμενο σύστημα αρχείων ext3, η συνάρτηση fsync() ξεπλύθηκε στο δίσκο όλων των "βρώμικων" σελίδων του συστήματος, και όχι μόνο εκείνων που σχετίζονται με το αντίστοιχο αρχείο. Αυτό σήμαινε ότι το κλικ σε ένα κουμπί στον Firefox θα μπορούσε να προκαλέσει την εγγραφή megabyte δεδομένων σε έναν μαγνητικό δίσκο, κάτι που θα μπορούσε να διαρκέσει πολλά δευτερόλεπτα. Η λύση στο πρόβλημα, από όσο κατάλαβα το υλικό, ήταν να μετακινηθεί η εργασία με τη βάση δεδομένων σε ασύγχρονες εργασίες φόντου. Αυτό σημαίνει ότι ο Firefox συνήθιζε να εφαρμόζει αυστηρότερες απαιτήσεις διατήρησης αποθήκευσης από ό,τι ήταν πραγματικά απαραίτητο και οι δυνατότητες του συστήματος αρχείων ext3 απλώς επιδείνωσαν αυτό το πρόβλημα.

Το δεύτερο πρόβλημα συνέβη το 2009. Στη συνέχεια, μετά από ένα σφάλμα συστήματος, οι χρήστες του νέου συστήματος αρχείων ext4 διαπίστωσαν ότι πολλά αρχεία που δημιουργήθηκαν πρόσφατα είχαν μηδενικό μήκος, αλλά αυτό δεν συνέβη με το παλαιότερο σύστημα αρχείων ext3. Στην προηγούμενη παράγραφο, μίλησα για το πώς το ext3 έριξε πάρα πολλά δεδομένα στο δίσκο, γεγονός που επιβράδυνε πολύ τα πράγματα. fsync(). Για να βελτιώσει την κατάσταση, το ext4 ξεπλένει μόνο εκείνες τις "βρώμικες" σελίδες που σχετίζονται με ένα συγκεκριμένο αρχείο. Και τα δεδομένα άλλων αρχείων παραμένουν στη μνήμη για πολύ μεγαλύτερο χρονικό διάστημα από ότι με το ext3. Αυτό έγινε για να βελτιωθεί η απόδοση (από προεπιλογή, τα δεδομένα παραμένουν σε αυτήν την κατάσταση για 30 δευτερόλεπτα, μπορείτε να το διαμορφώσετε χρησιμοποιώντας dirty_expire_centisecs; εδώ μπορείτε να βρείτε περισσότερες πληροφορίες σχετικά με αυτό). Αυτό σημαίνει ότι ένας μεγάλος όγκος δεδομένων μπορεί να χαθεί ανεπανόρθωτα μετά από μια συντριβή. Η λύση σε αυτό το πρόβλημα είναι η χρήση fsync() σε εφαρμογές που πρέπει να παρέχουν σταθερή αποθήκευση δεδομένων και να τα προστατεύουν όσο το δυνατόν περισσότερο από τις συνέπειες των αστοχιών. Λειτουργία fsync() λειτουργεί πολύ πιο αποτελεσματικά με το ext4 παρά με το ext3. Το μειονέκτημα αυτής της προσέγγισης είναι ότι η χρήση της, όπως και πριν, επιβραδύνει ορισμένες λειτουργίες, όπως η εγκατάσταση προγραμμάτων. Δείτε λεπτομέρειες για αυτό εδώ и εδώ.

Το τρίτο πρόβλημα σχετικά με fsync(), δημιουργήθηκε το 2018. Στη συνέχεια, στο πλαίσιο του έργου PostgreSQL, διαπιστώθηκε ότι εάν η συνάρτηση fsync() αντιμετωπίζει ένα σφάλμα, επισημαίνει τις "βρώμικες" σελίδες ως "καθαρές". Ως αποτέλεσμα, οι ακόλουθες κλήσεις fsync() να μην κάνεις τίποτα με τέτοιες σελίδες. Εξαιτίας αυτού, οι τροποποιημένες σελίδες αποθηκεύονται στη μνήμη και δεν εγγράφονται ποτέ στο δίσκο. Αυτό είναι μια πραγματική καταστροφή, επειδή η εφαρμογή θα πιστεύει ότι ορισμένα δεδομένα είναι γραμμένα στο δίσκο, αλλά στην πραγματικότητα δεν θα είναι. Τέτοιες αποτυχίες fsync() είναι σπάνιες, η εφαρμογή σε τέτοιες καταστάσεις δεν μπορεί να κάνει σχεδόν τίποτα για την καταπολέμηση του προβλήματος. Αυτές τις μέρες, όταν συμβαίνει αυτό, η PostgreSQL και άλλες εφαρμογές διακόπτονται. Εδώ, στο άρθρο "Μπορούν οι εφαρμογές να ανακτήσουν από αποτυχίες fsync;", αυτό το πρόβλημα διερευνάται λεπτομερώς. Επί του παρόντος, η καλύτερη λύση σε αυτό το πρόβλημα είναι η χρήση Direct I/O με τη σημαία O_SYNC ή με σημαία O_DSYNC. Με αυτήν την προσέγγιση, το σύστημα θα αναφέρει σφάλματα που ενδέχεται να προκύψουν κατά την εκτέλεση συγκεκριμένων λειτουργιών εγγραφής δεδομένων, αλλά αυτή η προσέγγιση απαιτεί από την εφαρμογή να διαχειρίζεται η ίδια τα buffer. Διαβάστε περισσότερα για αυτό εδώ и εδώ.

Άνοιγμα αρχείων χρησιμοποιώντας τις σημαίες O_SYNC και O_DSYNC

Ας επιστρέψουμε στη συζήτηση των μηχανισμών Linux που παρέχουν μόνιμη αποθήκευση δεδομένων. Δηλαδή, μιλάμε για χρήση της σημαίας O_SYNC ή σημαία O_DSYNC όταν ανοίγετε αρχεία χρησιμοποιώντας κλήση συστήματος Άνοιξε(). Με αυτήν την προσέγγιση, κάθε λειτουργία εγγραφής δεδομένων εκτελείται σαν μετά από κάθε εντολή write() δίνονται στο σύστημα, αντίστοιχα, εντολές fsync() и fdatasync(). Σε Προδιαγραφές POSIX αυτό ονομάζεται "Συγχρονισμένη ολοκλήρωση ακεραιότητας αρχείου I/O" και "Ολοκλήρωση ακεραιότητας δεδομένων". Το κύριο πλεονέκτημα αυτής της προσέγγισης είναι ότι χρειάζεται να εκτελεστεί μόνο μία κλήση συστήματος για να διασφαλιστεί η ακεραιότητα των δεδομένων και όχι δύο (για παράδειγμα − write() и fdatasync()). Το κύριο μειονέκτημα αυτής της προσέγγισης είναι ότι όλες οι λειτουργίες εγγραφής που χρησιμοποιούν τον αντίστοιχο περιγραφέα αρχείου θα συγχρονίζονται, γεγονός που μπορεί να περιορίσει τη δυνατότητα δομής του κώδικα εφαρμογής.

Χρήση Direct I/O με τη σημαία O_DIRECT

Κλήση συστήματος open() υποστηρίζει τη σημαία O_DIRECT, το οποίο έχει σχεδιαστεί για να παρακάμπτει την προσωρινή μνήμη του λειτουργικού συστήματος, να εκτελεί λειτουργίες I/O, αλληλεπιδρώντας απευθείας με το δίσκο. Αυτό, σε πολλές περιπτώσεις, σημαίνει ότι οι εντολές εγγραφής που εκδίδονται από το πρόγραμμα θα μεταφραστούν απευθείας σε εντολές που στοχεύουν στην εργασία με το δίσκο. Αλλά, γενικά, αυτός ο μηχανισμός δεν αντικαθιστά τις λειτουργίες fsync() ή fdatasync(). Το γεγονός είναι ότι ο ίδιος ο δίσκος μπορεί καθυστέρηση ή προσωρινή αποθήκευση κατάλληλες εντολές για την εγγραφή δεδομένων. Και, ακόμη χειρότερα, σε ορισμένες ειδικές περιπτώσεις, οι λειτουργίες I/O εκτελούνται κατά τη χρήση της σημαίας O_DIRECT, αναμετάδοση σε παραδοσιακές λειτουργίες προσωρινής αποθήκευσης. Ο ευκολότερος τρόπος για να λύσετε αυτό το πρόβλημα είναι να χρησιμοποιήσετε τη σημαία για να ανοίξετε αρχεία O_DSYNC, που θα σημαίνει ότι κάθε λειτουργία εγγραφής θα ακολουθείται από μια κλήση fdatasync().

Αποδείχθηκε ότι το σύστημα αρχείων XFS είχε πρόσφατα προσθέσει μια "γρήγορη διαδρομή" για O_DIRECT|O_DSYNC-αρχεία δεδομένων. Εάν το μπλοκ αντικατασταθεί χρησιμοποιώντας O_DIRECT|O_DSYNC, τότε το XFS, αντί να ξεπλύνει την κρυφή μνήμη, θα εκτελέσει την εντολή εγγραφής FUA εάν η συσκευή την υποστηρίζει. Το επιβεβαίωσα χρησιμοποιώντας το βοηθητικό πρόγραμμα blktrace σε σύστημα Linux 5.4/Ubuntu 20.04. Αυτή η προσέγγιση θα πρέπει να είναι πιο αποτελεσματική, καθώς εγγράφει την ελάχιστη ποσότητα δεδομένων στο δίσκο και χρησιμοποιεί μία λειτουργία, όχι δύο (εγγραφή και εκκαθάριση της προσωρινής μνήμης). Βρήκα έναν σύνδεσμο προς patch 2018 πυρήνας που υλοποιεί αυτόν τον μηχανισμό. Υπάρχει κάποια συζήτηση σχετικά με την εφαρμογή αυτής της βελτιστοποίησης σε άλλα συστήματα αρχείων, αλλά από όσο γνωρίζω, το XFS είναι το μόνο σύστημα αρχείων που το υποστηρίζει μέχρι στιγμής.

συνάρτηση sync_file_range().

Το Linux έχει μια κλήση συστήματος sync_file_range(), το οποίο σας επιτρέπει να ξεπλύνετε μόνο μέρος του αρχείου στο δίσκο και όχι ολόκληρο το αρχείο. Αυτή η κλήση ξεκινά μια ασύγχρονη έκπλυση και δεν περιμένει να ολοκληρωθεί. Αλλά στην αναφορά σε sync_file_range() αυτή η εντολή λέγεται ότι είναι «πολύ επικίνδυνη». Δεν συνιστάται η χρήση του. Χαρακτηριστικά και κίνδυνοι sync_file_range() πολύ καλά περιγράφεται στο Αυτό υλικό. Συγκεκριμένα, αυτή η κλήση φαίνεται να χρησιμοποιεί το RocksDB για να ελέγχει πότε ο πυρήνας ξεπλένει "βρώμικα" δεδομένα στο δίσκο. Αλλά την ίδια στιγμή εκεί, για να εξασφαλιστεί σταθερή αποθήκευση δεδομένων, χρησιμοποιείται επίσης fdatasync(). Σε κώδικας Η RocksDB έχει μερικά ενδιαφέροντα σχόλια για αυτό το θέμα. Για παράδειγμα, μοιάζει με την κλήση sync_file_range() όταν χρησιμοποιείτε το ZFS δεν ξεπλένει δεδομένα στο δίσκο. Η εμπειρία μου λέει ότι ο σπάνια χρησιμοποιούμενος κώδικας μπορεί να περιέχει σφάλματα. Επομένως, θα σας συμβούλευα να μην χρησιμοποιήσετε αυτήν την κλήση συστήματος εκτός εάν είναι απολύτως απαραίτητο.

Κλήσεις συστήματος για να διασφαλιστεί η διατήρηση των δεδομένων

Κατέληξα στο συμπέρασμα ότι υπάρχουν τρεις προσεγγίσεις που μπορούν να χρησιμοποιηθούν για την εκτέλεση μόνιμων λειτουργιών I/O. Όλα απαιτούν κλήση λειτουργίας fsync() για τον κατάλογο όπου δημιουργήθηκε το αρχείο. Αυτές είναι οι προσεγγίσεις:

  1. Κλήση λειτουργίας fdatasync() ή fsync() μετά τη λειτουργία write() (καλύτερα να το χρησιμοποιήσετε fdatasync()).
  2. Εργασία με έναν περιγραφέα αρχείου που ανοίγει με μια σημαία O_DSYNC ή O_SYNC (καλύτερα - με σημαία O_DSYNC).
  3. Χρήση εντολών pwritev2() με σημαία RWF_DSYNC ή RWF_SYNC (κατά προτίμηση με σημαία RWF_DSYNC).

Σημειώσεις Απόδοσης

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

  1. Η αντικατάσταση δεδομένων αρχείου είναι ταχύτερη από την προσθήκη δεδομένων σε ένα αρχείο (το κέρδος απόδοσης μπορεί να είναι 2-100%). Η επισύναψη δεδομένων σε ένα αρχείο απαιτεί πρόσθετες αλλαγές στα μεταδεδομένα του αρχείου, ακόμη και μετά την κλήση συστήματος fallocate(), αλλά το μέγεθος αυτής της επίδρασης μπορεί να διαφέρει. Συνιστώ, για καλύτερη απόδοση, να καλέσετε fallocate() να προκατανεμηθεί ο απαιτούμενος χώρος. Τότε αυτό το διάστημα πρέπει να γεμίσει ρητά με μηδενικά και να κληθεί fsync(). Αυτό θα έχει ως αποτέλεσμα τα αντίστοιχα μπλοκ στο σύστημα αρχείων να επισημανθούν ως "κατανεμημένα" αντί για "μη εκχωρημένα". Αυτό δίνει μια μικρή (περίπου 2%) βελτίωση της απόδοσης. Επίσης, ορισμένοι δίσκοι μπορεί να έχουν πιο αργή λειτουργία πρώτου μπλοκ πρόσβασης από άλλους. Αυτό σημαίνει ότι η πλήρωση του χώρου με μηδενικά μπορεί να οδηγήσει σε σημαντική (περίπου 100%) βελτίωση της απόδοσης. Συγκεκριμένα, αυτό μπορεί να συμβεί με δίσκους. AWS EBS (αυτά είναι ανεπίσημα στοιχεία, δεν μπόρεσα να τα επιβεβαιώσω). Το ίδιο ισχύει και για την αποθήκευση. Μόνιμος δίσκος GCP (και αυτό είναι ήδη επίσημη πληροφορία, επιβεβαιωμένη από δοκιμές). Το ίδιο έχουν κάνει και άλλοι ειδικοί παρατηρήσειςπου σχετίζονται με διαφορετικούς δίσκους.
  2. Όσο λιγότερες κλήσεις συστήματος, τόσο υψηλότερη είναι η απόδοση (το κέρδος μπορεί να είναι περίπου 5%). Μοιάζει με κλήση open() με σημαία O_DSYNC ή καλέστε pwritev2() με σημαία RWF_SYNC ταχύτερη κλήση fdatasync(). Υποψιάζομαι ότι το θέμα εδώ είναι ότι με αυτήν την προσέγγιση, το γεγονός ότι πρέπει να εκτελεστούν λιγότερες κλήσεις συστήματος για την επίλυση της ίδιας εργασίας (μία κλήση αντί για δύο) παίζει ρόλο. Όμως η διαφορά απόδοσης είναι πολύ μικρή, οπότε μπορείς εύκολα να την αγνοήσεις και να χρησιμοποιήσεις κάτι στην εφαρμογή που δεν οδηγεί στην περιπλοκή της λογικής της.

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

  • Μέθοδοι πρόσβασης I/O — επισκόπηση των βασικών μηχανισμών εισόδου/εξόδου.
  • Διασφάλιση ότι τα δεδομένα φτάνουν στο δίσκο - μια ιστορία για το τι συμβαίνει στα δεδομένα κατά τη διαδρομή από την εφαρμογή στο δίσκο.
  • Πότε πρέπει να συγχρονίσετε τον κατάλογο που περιέχει - η απάντηση στο ερώτημα πότε να υποβάλετε αίτηση fsync() για καταλόγους. Με λίγα λόγια, αποδεικνύεται ότι πρέπει να το κάνετε αυτό όταν δημιουργείτε ένα νέο αρχείο και ο λόγος για αυτήν τη σύσταση είναι ότι στο Linux μπορεί να υπάρχουν πολλές αναφορές στο ίδιο αρχείο.
  • SQL Server σε Linux: FUA Internals - εδώ είναι μια περιγραφή του τρόπου με τον οποίο υλοποιείται η μόνιμη αποθήκευση δεδομένων στον SQL Server στην πλατφόρμα Linux. Υπάρχουν μερικές ενδιαφέρουσες συγκρίσεις μεταξύ κλήσεων συστήματος Windows και Linux εδώ. Είμαι σχεδόν σίγουρος ότι χάρη σε αυτό το υλικό έμαθα για τη βελτιστοποίηση FUA του XFS.

Έχετε χάσει ποτέ δεδομένα που νομίζατε ότι ήταν αποθηκευμένα με ασφάλεια στο δίσκο;

Ανθεκτική αποθήκευση δεδομένων και API αρχείων Linux

Ανθεκτική αποθήκευση δεδομένων και API αρχείων Linux

Πηγή: www.habr.com