Μετάβαση στο περιεχόμενο
EuroVPSEuroVPS
Περιοχή πελατών

Ένα ολόκληρο δίκτυο websites αργούσε ταυτόχρονα, και ούτε ένας server δεν ήταν απασχολημένος.

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

Από EuroVPS EngineeringΤο μηχανοστάσιο

5 λεπτά ανάγνωσης
Σε αυτό το άρθρο 8 ενότητες
Εν συντομία
  • Κάθε site σε ένα δίκτυο αγγελιών εργασίας επιβραδύνθηκε ταυτόχρονα ενώ οι servers κάθονταν αδρανείς, και το ένστικτο ήταν να αγοραστούν μεγαλύτεροι.
  • Κάθε επίπεδο ήταν αδρανές εκτός από ένα: οι load balancers και οι web servers μετά βίας δούλευαν. Όλη η φάρμα περίμενε τη βάση δεδομένων, και η βάση περίμενε ένα query.
  • Αυτό το query, μια απλή αναζήτηση πόλης, ήταν περισσότερο από το μισό του συνολικού χρόνου της βάσης, σαρώνοντας ολόκληρο πίνακα σε κάθε κλήση για να επιστρέψει μία γραμμή.
  • Ένα online index, που εφαρμόστηκε χωρίς διακοπή των sites, μετέτρεψε το scan σε seek. Δεν χρειάστηκαν μεγαλύτεροι servers.

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

Κάθε επίπεδο ήταν αδρανές εκτός από ένα

Σε μια επιβράδυνση ολόκληρης φάρμας, διαβάστε τη χρήση κάθε επιπέδου πριν από οτιδήποτε άλλο, και δείτε ποιο είναι πραγματικά καρφωμένο. Οι load balancers χάζευαν. Οι web servers βαριόντουσαν. Τίποτα στη διαδρομή του request δεν δούλευε αρκετά σκληρά για να εξηγήσει ένα αργό site.

Η ΦΑΡΜΑ, ΜΕΤΡΗΜΕΝΗ
Load balancers
αδρανείς
Web servers
μετά βίας δούλευαν
Βάση δεδομένων
περίμενε ένα query

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

Η ακριβή απάντηση ήταν να αγοραστούν περισσότερα μηχανήματα

Το ένστικτοΗ μέτρηση
Όλη η φάρμα είναι αργή, άρα οι servers είναι υπερφορτωμένοι. Αγοράστε μεγαλύτερους.Κάθε επίπεδο εκτός από τη βάση δεδομένων ήταν αδρανές. Η χωρητικότητα δεν ήταν ποτέ ο περιορισμός.
ΤΙ ΕΛΕΓΑΝ ΤΑ ΔΕΔΟΜΕΝΑΠρέπει να είναι πρόβλημα hardware.Το hardware ήταν υγιές και αποδεδειγμένα: load balancers και web servers μετά βίας δούλευαν.
Μοιράστε το φορτίο σε περισσότερα μηχανήματα.Ένα query ήταν περισσότερο από το μισό του συνολικού χρόνου της βάσης. Περισσότερα μηχανήματα θα το έτρεχαν πιο συχνά, πάλι σαρώνοντας.

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

Ομαδοποιήσαμε το ιστορικό των queries κατά αποτύπωμα

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

AGGREGATE BY FINGERPRINT, RANK BY SHARE OF DB TIME
-- reconstructed, de-identified rendering of cleared facts. not a real capture.
-- full query history, grouped by query_hash
-- (a fingerprint that survives inconsistent parameterisation)
SELECT TOP 5
  query_hash,
  SUM(execution_count)                AS calls,
  SUM(total_logical_reads)            AS pages_read,
  SUM(total_elapsed_time) * 100.0
    / SUM(SUM(total_elapsed_time)) OVER () AS pct_of_db_time
FROM   sys.dm_exec_query_stats
GROUP  BY query_hash
ORDER  BY pct_of_db_time DESC;

Μία γραμμή ξεχώρισε: περισσότερο από το μισό από όσα έκανε η βάση, ξοδεμένο σε ένα statement. Η μέθοδος είναι σκόπιμα βαρετή, δεν μαντεύει, και είναι η ίδια κάθε φορά.

Ένα query ήταν περισσότερο από το μισό της βάσης

Το statement που έπεσε έξω είναι από αυτά που δεν θα κοιτούσατε ποτέ δεύτερη φορά. Μια αναζήτηση πόλης, δύο στήλες, ένα φίλτρο.

THE QUERY AT THE TOP OF THE LIST
-- reconstructed, de-identified. table/column names renamed from the real schema.
SELECT CityID, CityName
FROM   Cities
WHERE  RoutingPath = @p;

-- Execution plan: Clustered Index Scan (a full scan)
--   Returns:          ONE row
--   Filtered column:  RoutingPath --> no index

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

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

“Μια απάντηση μίας γραμμής πρέπει να διαβάζει δύο ή τρεις σελίδες, όχι εκατοντάδες.”

Η ΓΡΑΜΜΗ ΠΟΥ ΣΥΝΟΨΙΖΕΙ ΤΗ ΔΙΑΓΝΩΣΗ

Αυτή η πρόταση είναι όλη η διάγνωση, συμπιεσμένη σε κάτι που μπορείτε να πείτε στον άνθρωπο που έχει τον κώδικα. Τίποτα άλλο δεν κάνει μια αναζήτηση μίας γραμμής να διαβάζει τόσο πολύ. Ούτε αργός δίσκος, ούτε απασχολημένη CPU, ούτε κακή βραδιά. Μια απάντηση μίας γραμμής που διαβάζει ολόκληρο τον πίνακα είναι full scan, και ένα full scan σε φιλτραρισμένη στήλη είναι αλάνθαστη υπογραφή: η στήλη χρειάζεται index. Δεν χρειάστηκε να διαφωνήσουμε για hardware, απλώς δείξαμε τα reads.

Ένα online index, από scan σε seek

ΚΑΘΕ ΚΛΗΣΗ, ΠΡΙΝ ΤΟ INDEX
  • Full scan του πίνακα, για μία γραμμή
  • Κανένα index στη φιλτραρισμένη στήλη
  • Περισσότερο από το μισό του συνολικού χρόνου της βάσης
ΚΑΘΕ ΚΛΗΣΗ, ΜΕΤΑ ΑΠΟ ΕΝΑ INDEX
  • Ένα index seek, δυο τρεις σελίδες για την ίδια γραμμή
  • Ένα μικρό κλάσμα της δουλειάς ανά κλήση
  • Το index μπήκε online, χωρίς διακοπή των sites
ONE ONLINE INDEX ON THE COLUMN THE APP ACTUALLY FILTERS ON
-- reconstructed, de-identified. index name illustrative, on the renamed identifiers.
-- Index the column in the WHERE clause; cover the two columns it returns
CREATE NONCLUSTERED INDEX IX_Cities_RoutingPath
  ON Cities (RoutingPath)
  INCLUDE (CityID, CityName)
  WITH (ONLINE = ON);   -- applied without interrupting the site

-- New execution plan: Index Seek

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

Ο μηχανικός του πελάτη πρόσθεσε στη διάγνωση

Το καλύτερο κομμάτι αυτής της συνεργασίας δεν ήταν το δικό μας index. Ήταν αυτό που έγινε όταν ο μηχανικός βάσεων δεδομένων του ίδιου του πελάτη μπήκε στη συζήτηση και πρόσθεσε στη διάγνωση: έφερε ένα μοτίβο blocking reader/writer από τη δική του πλευρά του συστήματος. Ενσωματώσαμε τη διόρθωσή του με τη δική μας, και οι δύο μπήκαν μαζί.

“Ο μηχανικός του πελάτη έφυγε από την κλήση συνεργάτης.”

ΠΩΣ ΠΡΕΠΕΙ ΝΑ ΤΕΛΕΙΩΝΟΥΝ ΑΥΤΕΣ ΟΙ ΚΛΗΣΕΙΣ

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

Όταν κάθε επίπεδο είναι αδρανές εκτός από ένα, σταματήστε να κοιτάτε τη χωρητικότητα

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

01

Διαβάστε πρώτα τη χρήση κάθε επιπέδου.

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

02

Ομαδοποιήστε τα στατιστικά των queries κατά αποτύπωμα.

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

03

Συγκρίνετε σελίδες που διαβάστηκαν με γραμμές που επιστράφηκαν.

Μια απάντηση μίας γραμμής που διαβάζει εκατοντάδες σελίδες σαρώνει, δεν αναζητά. Αυτό το χάσμα είναι η υπογραφή ενός index που λείπει.

04

Βάλτε index στη στήλη στην οποία πραγματικά φιλτράρει η εφαρμογή.

Όχι σε κάθε στήλη. Σε αυτήν στο WHERE που τρέχει εκατομμύρια φορές. Εφαρμόστε το online, χωρίς διακοπή του site.

05

Κάντε cache τους μικροσκοπικούς καυτούς πίνακες.

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

Σε όλη τη διάρκεια, το μηχάνημα έμεινε ήρεμο. Οι load balancers χάζευαν, οι web servers κάθονταν βαριεστημένοι, και η διόρθωση ήταν ένα index στη στήλη στην οποία φιλτράριζε η εφαρμογή. Ο database server ήταν υγιής· ένα statement μέσα του χρειαζόταν index.

Ευθείες απαντήσεις

Πώς μπορεί ένα query να είναι περισσότερο από το μισό φορτίο μιας βάσης δεδομένων;+

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

Γιατί οι servers ήταν αδρανείς αν τα sites ήταν αργά;+

Γιατί το bottleneck ήταν μέσα στη βάση δεδομένων, περιμένοντας ένα query. Οι load balancers και οι web servers μετά βίας δούλευαν. Όλη η φάρμα περίμενε τη βάση, και η βάση περίμενε μια αναζήτηση που έπρεπε να είναι ακαριαία. Η προσθήκη servers θα πρόσθετε μόνο περισσότερα αδρανή μηχανήματα.

Πώς βρίσκετε το query που είναι πραγματικά το πρόβλημα;+

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

Ποια είναι η υπογραφή ενός index που λείπει;+

Μια απάντηση μίας γραμμής που διαβάζει εκατοντάδες σελίδες. Αν ένα query επιστρέφει μία γραμμή και το plan δείχνει scan σε ολόκληρο τον πίνακα, η στήλη στην οποία φιλτράρει δεν έχει index.

Η διόρθωση απαίτησε downtime;+

Όχι. Το index μπήκε online, εφαρμόστηκε χωρίς διακοπή των sites, και μετέτρεψε το scan σε seek.

Ποιος το διόρθωσε, εσείς ή ο πελάτης;+

Και οι δύο, μαζί. Εμείς βρήκαμε την αναζήτηση και προσθέσαμε το index· ο μηχανικός βάσεων δεδομένων του ίδιου του πελάτη μπήκε στη διάγνωση και έφερε ένα μοτίβο blocking reader/writer από τη δική του πλευρά. Ενσωματώσαμε τη διόρθωσή του με τη δική μας.

Αργό site, αδρανείς servers; Θα βρούμε την πραγματική αιτία.

Μιλήστε με έναν μηχανικό που διαβάζει query plans, όχι μόνο dashboards, για το τι πραγματικά επιβραδύνει την πλατφόρμα σας.

Μιλήστε με έναν μηχανικό

Το ένστικτο σε μια αργή φάρμα είναι να αγοράσετε τον δρόμο της εξόδου. Η έντιμη απάντηση είναι σχεδόν πάντα φθηνότερη: διαβάστε κάθε επίπεδο, βρείτε αυτό που είναι καρφωμένο, και πηγαίνετε να διαβάσετε τα queries του.

Φτάσατε στο τέλος.

Βάλτε την ανάγνωση σε εφαρμογή

Τι τρέχετε;

Πείτε μας για τον φόρτο εργασίας, την κίνηση και τις απαιτήσεις ανάκαμψης. Ξεκινήστε από το εργαλείο διαμόρφωσης server ή συζητήστε τις λεπτομέρειες με έναν μηχανικό.

Γραμμένο από τους μηχανικούς που χτίζουν και λειτουργούν την υποδομή που περιγράφει.
Μιλήστε με έναν μηχανικό
XIn

5 λεπτά ακόμη