Μια σελίδα που απαντά σε 200 ms απέναντι σε τοπικό Redis χρειάζεται τέσσερα δευτερόλεπτα μόλις ο πελάτης βρεθεί πίσω από VPN, ενώ οι μετρητές του διακομιστή μένουν αδρανείς. Το αργό αρχείο καταγραφής δεν δείχνει τίποτα πάνω από ένα χιλιοστό του δευτερολέπτου, το γράφημα CPU είναι επίπεδο και η υπηρεσία εξυπηρετεί άλλους πελάτες χωρίς παράπονο. Αυτό που άλλαξε είναι η απόσταση, και αυτό που πολλαπλασιάζει την απόσταση είναι ο αριθμός των φορών που ο κώδικάς σας σταματά και περιμένει. Περπατήστε τη διαδρομή του αιτήματος από τον πελάτη προς τα έξω και η λύση είναι σχεδόν πάντα λιγότερες διαδοχικές αναμονές παρά γρηγορότερος διακομιστής.

Ξεκινήστε από τον πελάτη, όχι από τις μετρήσεις του διακομιστή

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

Μετρήστε την ίδια τη διαδρομή πριν αγγίξετε κώδικα. Το redis-cli -h redis.example.internal --latency στέλνει επαναλαμβανόμενα PING και εκτυπώνει τον μέσο χρόνο αιτήματος και απάντησης, που είναι το κόστος σας ανά ανταλλαγή. Πολλαπλασιάστε το με τον αριθμό των διαδοχικών εντολών σε μία ενέργεια χρήστη και έχετε ένα κατώτατο όριο για το endpoint.

Μετρήστε τις διαδρομές που χρειάζεται πραγματικά μία ενέργεια χρήστη

Πάρτε ένα μόνο endpoint και γράψτε κάθε κλήση Redis με σειρά, μαζί με όσες κρύβονται μέσα σε ORM, middleware συνεδρίας ή αναζήτηση feature flag. Τα περισσότερα αργά endpoints κάνουν είκοσι ή τριάντα κλήσεις εκεί που θα αρκούσαν τρεις, και οι κρυφές είναι συνήθως η πλειονότητα.

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

Ο αριθμός που μετράει είναι οι διαδοχικές αναμονές, όχι οι συνολικές εντολές. Τριάντα εντολές σε μία παρτίδα κοστίζουν ακόμα μία αναμονή.

Ομαδοποιήστε μόνο τις εντολές που δεν εξαρτώνται μεταξύ τους

Ένας αγωγός βάζει σε ουρά ανεξάρτητες εντολές και συλλέγει τις απαντήσεις με σειρά, μετατρέποντας N αναμονές σε μία. Οι περισσότεροι πελάτες το εκθέτουν ως μπλοκ, callback ή διαχειριστή περιβάλλοντος αντί για ξεχωριστή λειτουργία σύνδεσης, οπότε ελέγξτε το API που παρέχει η εγκατεστημένη σας έκδοση. Το κέρδος κλιμακώνεται με τον χρόνο διαδρομής, γι' αυτό ακριβώς μετράει σε VPN και σχεδόν δεν φαίνεται στο localhost.

import redis

r = redis.Redis(host="redis.example.internal", port=6379, socket_timeout=2)
pipe = r.pipeline(transaction=False)
for key in ("user:1001", "user:1002", "user:1003"):
    pipe.get(key)
print(pipe.execute())

Περάστε transaction=False όταν θέλετε μόνο ομαδοποίηση. Με την προεπιλογή ο πελάτης τυλίγει την παρτίδα σε MULTI και EXEC, που την κάνει ατομική και αλλάζει τι μπορούν να παρατηρήσουν άλλοι πελάτες στη διάρκεια. Η ατομικότητα είναι απόφαση ορθότητας, όχι ρυθμιστικό απόδοσης, και ένας αγωγός δεν είναι συναλλαγή μόνο επειδή και τα δύο μειώνουν τις διαδρομές.

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

Κρατήστε τη σύνδεση ζωντανή ανάμεσα στις ενέργειες

Κάθε νέα σύνδεση πληρώνει επίλυση ονομάτων, χειραψία TCP, χειραψία TLS και AUTH, και πάνω από VPN καθένα από αυτά είναι διαδρομή που δεν μπορείτε να ομαδοποιήσετε. Δημιουργήστε τον πελάτη μία φορά ανά διεργασία και επαναχρησιμοποιήστε τον, ή χρησιμοποιήστε το pool που παρέχει η βιβλιοθήκη σας με φραγμένο μέγεθος. Η επανασύνδεση ανά αίτημα είναι η δεύτερη πιο συχνή αιτία αυτού του συμπτώματος μετά τις αναμονές ανά εντολή.

Κρατήστε το ιδιωτικό όνομα κεντρικού υπολογιστή στη διαμόρφωση αντί για κυριολεκτική διεύθυνση. Η επικύρωση πιστοποιητικού και η ταυτότητα υπηρεσίας εξακολουθούν να ισχύουν σε ιδιωτικό δίκτυο, και η καρφίτσωμα μιας IP σπάει τη στιγμή που ο πάροχος αναριθμεί ένα υποδίκτυο. Αν η επίλυση είναι το αργό μέρος, διορθώστε τη διαδρομή του resolver αντί για το connection string.

Ορίστε ρητό socket timeout ώστε μια κολλημένη σήραγγα να αποτυγχάνει γρήγορα αντί να κρεμάει έναν εργάτη. Έπειτα επιβεβαιώστε ότι το pool πραγματικά επαναχρησιμοποιείται καταγράφοντας τη δημιουργία συνδέσεων στον πελάτη.

Δώστε μέγεθος στην παρτίδα πριν γίνει πρόβλημα μνήμης

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

Επιλέξτε μέγεθος παρτίδας με βάση τον όγκο απάντησης και όχι τον αριθμό εντολών. Λίγες εκατοντάδες μικρές εντολές σε μία παρτίδα είναι συνήθως άνετες, ενώ εκατό φορτία πολλών kilobyte δεν είναι. Χωρίστε κατά byte καθώς και κατά πλήθος, και κρατήστε κάθε παρτίδα μέσα στον προϋπολογισμό καθυστέρησης που έχει πραγματικά το endpoint.

Μετρήστε από άκρη σε άκρη μετά την αλλαγή: το redis-cli -h redis.example.internal --latency-history δείχνει αν η ίδια η διαδρομή είναι σταθερή, και το δικό σας χρονόμετρο αιτημάτων δείχνει αν το endpoint βελτιώθηκε. Αν το endpoint παραμένει αργό ενώ το ιστορικό καθυστέρησης είναι επίπεδο, το υπόλοιπο κόστος είναι εργασία στον διακομιστή ή η δική σας σειριοποίηση, και το επόμενο βήμα είναι προφίλ του handler αντί για άλλη αλλαγή ομαδοποίησης.

Σχετικά άρθρα