Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Σας προτείνω να διαβάσετε την απομαγνητοφώνηση της αναφοράς του Vladimir Sitnikov στις αρχές του 2016 "PostgreSQL και JDBC στύβουν όλο το ζουμί"

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Καλό απόγευμα Το όνομά μου είναι Βλαντιμίρ Σίτνικοφ. Δουλεύω για το NetCracker εδώ και 10 χρόνια. Και είμαι κυρίως στην παραγωγικότητα. Ό,τι σχετίζεται με την Java, ό,τι σχετίζεται με την SQL είναι αυτό που αγαπώ.

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Θα μιλήσουμε:

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Ας ξεκινήσουμε με μια απλή ερώτηση. Επιλέγουμε μία σειρά από τον πίνακα με βάση το πρωτεύον κλειδί.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Η βάση δεδομένων βρίσκεται στον ίδιο κεντρικό υπολογιστή. Και όλη αυτή η καλλιέργεια διαρκεί 20 χιλιοστά του δευτερολέπτου.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Αυτά τα 20 χιλιοστά του δευτερολέπτου είναι πολλά. Εάν έχετε 100 τέτοια αιτήματα, τότε ξοδεύετε χρόνο ανά δευτερόλεπτο κάνοντας κύλιση σε αυτά τα αιτήματα, δηλαδή χάνουμε χρόνο.

Δεν μας αρέσει να το κάνουμε αυτό και να δούμε τι μας προσφέρει η βάση για αυτό. Η βάση δεδομένων μας προσφέρει δύο επιλογές για την εκτέλεση ερωτημάτων.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Η πρώτη επιλογή είναι ένα απλό αίτημα. Τι καλό έχει; Το ότι το παίρνουμε και το στέλνουμε και τίποτα παραπάνω.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

https://github.com/pgjdbc/pgjdbc/pull/478

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

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Και αυτό που μπορούμε να κάνουμε είναι απλό ερώτημα και εκτεταμένο ερώτημα.

Τι είναι το ιδιαίτερο για κάθε προσέγγιση;

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Ας προχωρήσουμε στην εξάσκηση. Έτσι μοιάζει μια τυπική εφαρμογή. Θα μπορούσε να είναι Java, κλπ.

Δημιουργήσαμε δήλωση. Εκτέλεσε την εντολή. Δημιουργήθηκε κοντά. Πού είναι το λάθος εδώ; Ποιο είναι το πρόβλημα? Κανένα πρόβλημα. Αυτό λέει σε όλα τα βιβλία. Έτσι πρέπει να γράφεται. Αν θέλετε μέγιστη απόδοση, γράψτε έτσι.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Αλλά η πρακτική έχει δείξει ότι αυτό δεν λειτουργεί. Γιατί; Γιατί έχουμε μέθοδο «κλειστή». Και όταν το κάνουμε αυτό, από την άποψη της βάσης δεδομένων αποδεικνύεται ότι είναι σαν ένας καπνιστής που δουλεύει με μια βάση δεδομένων. Είπαμε "ΑΝΑΛΥΣΗ ΕΚΤΕΛΕΣΤΕ ΑΠΟΣΤΟΛΗ".

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Πώς μπορούμε να το πετύχουμε αυτό;

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Πώς να εργαστείτε σωστά; Τι χρειάζεται να κάνουμε για αυτό;

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

Και η PostgreSQL δεν ξέρει πώς να αποθηκεύει ερωτήματα προσωρινής αποθήκευσης. Είναι απαραίτητο κάθε συνεδρία να δημιουργεί αυτήν την κρυφή μνήμη για τον εαυτό της.

Και δεν θέλουμε να χάνουμε χρόνο ούτε στην ανάλυση.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Και ως συνήθως έχουμε δύο επιλογές.

Η πρώτη επιλογή είναι να το πάρουμε και να πούμε ότι ας τυλίξουμε τα πάντα σε PgSQL. Υπάρχει μια κρυφή μνήμη εκεί. Αποθηκεύει τα πάντα. Θα βγει υπέροχο. Το είδαμε αυτό. Έχουμε 100500 αιτήματα. Δεν δουλεύει. Δεν συμφωνούμε να μετατρέψουμε τα αιτήματα σε διαδικασίες χειροκίνητα. Οχι όχι.

Έχουμε μια δεύτερη επιλογή - πάρτε το και κόψτε το μόνοι μας. Ανοίγουμε τις πηγές και αρχίζουμε να κόβουμε. Είδαμε και είδαμε. Αποδείχθηκε ότι δεν είναι τόσο δύσκολο να γίνει.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

https://github.com/pgjdbc/pgjdbc/pull/319

Αυτό εμφανίστηκε τον Αύγουστο του 2015. Τώρα υπάρχει μια πιο μοντέρνα έκδοση. Και όλα είναι υπέροχα. Λειτουργεί τόσο καλά που δεν αλλάζουμε τίποτα στην εφαρμογή. Και σταματήσαμε να σκεφτόμαστε προς την κατεύθυνση της PgSQL, δηλαδή αυτό ήταν αρκετό για να μειώσουμε όλα τα γενικά έξοδα σχεδόν στο μηδέν.

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Μπορείτε να ρωτήσετε - πού είναι οι αριθμοί; Τι παίρνετε; Και εδώ δεν θα δώσω αριθμούς, γιατί κάθε αίτημα έχει το δικό του.

Τα ερωτήματά μας ήταν τέτοια που ξοδέψαμε περίπου 20 χιλιοστά του δευτερολέπτου για ανάλυση σε ερωτήματα OLTP. Υπήρχαν 0,5 χιλιοστά του δευτερολέπτου για εκτέλεση, 20 χιλιοστά του δευτερολέπτου για ανάλυση. Αίτημα – 10 KiB κειμένου, 170 γραμμές σχεδίου. Αυτό είναι ένα αίτημα OLTP. Ζητάει 1, 5, 10 γραμμές, μερικές φορές περισσότερες.

Αλλά δεν θέλαμε να χάσουμε καθόλου 20 χιλιοστά του δευτερολέπτου. Το μειώσαμε στο 0. Όλα είναι υπέροχα.

Τι μπορείτε να αφαιρέσετε από εδώ; Αν έχεις Java, τότε παίρνεις τη σύγχρονη έκδοση του προγράμματος οδήγησης και χαίρεσαι.

Εάν μιλάτε διαφορετική γλώσσα, τότε σκεφτείτε - ίσως το χρειάζεστε και αυτό; Διότι από την άποψη της τελικής γλώσσας, για παράδειγμα, εάν έχετε PL 8 ή έχετε LibPQ, τότε δεν είναι προφανές για εσάς ότι ξοδεύετε χρόνο όχι στην εκτέλεση, στην ανάλυση, και αυτό αξίζει να το ελέγξετε. Πως? Όλα είναι δωρεάν.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Εάν το αίτημα δημιουργείται δυναμικά. Συμβαίνει. Κάποιος κολλάει τις συμβολοσειρές μεταξύ τους, με αποτέλεσμα ένα ερώτημα SQL.

Γιατί είναι κακός; Είναι κακό γιατί κάθε φορά καταλήγουμε με διαφορετική χορδή.

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Επόμενο πρόβλημα. Οι τύποι δεδομένων είναι σημαντικοί. Υπάρχουν ORM που λένε ότι δεν έχει σημασία τι είδους NULL υπάρχει, ας υπάρχει κάποιο είδος. Αν Int, τότε λέμε setInt. Και αν NULL, τότε ας είναι πάντα VARCHAR. Και τι διαφορά έχει τελικά τι NULL υπάρχει; Η ίδια η βάση δεδομένων θα καταλάβει τα πάντα. Και αυτή η εικόνα δεν λειτουργεί.

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Εντάξει, ενεργοποιήθηκε. Ίσως πήραν τον οδηγό. Και η παραγωγικότητα έπεσε. Τα πράγματα έγιναν άσχημα.

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

https://gist.github.com/vlsi/df08cbef370b2e86a5c1

Το θέμα είναι ότι έχουμε δύο στήλες, καθεμία από τις οποίες είναι ευρετηριασμένη. Υπάρχουν ένα εκατομμύριο σειρές σε μία στήλη NULL. Και η δεύτερη στήλη περιέχει μόνο 20 γραμμές. Όταν εκτελούμε χωρίς δεσμευμένες μεταβλητές, όλα λειτουργούν καλά.

Αν ξεκινήσουμε την εκτέλεση με δεσμευμένες μεταβλητές, δηλαδή εκτελέσουμε το "?" ή "1$" για το αίτημά μας, τι θα λάβουμε τελικά;

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

https://gist.github.com/vlsi/df08cbef370b2e86a5c1

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Τι μπορείτε να κάνετε για αυτό; Εδώ, βέβαια, είναι πιο δύσκολο να υποθέσουμε οτιδήποτε. Υπάρχει μια απλή λύση που χρησιμοποιούμε. Αυτό είναι +0, OFFSET 0. Σίγουρα γνωρίζετε τέτοιες λύσεις. Απλώς το παίρνουμε και προσθέτουμε "+0" στο αίτημα και όλα είναι καλά. Θα σας δείξω αργότερα.

Και υπάρχει μια άλλη επιλογή - κοιτάξτε τα σχέδια πιο προσεκτικά. Ο προγραμματιστής πρέπει όχι μόνο να γράψει ένα αίτημα, αλλά και να πει "explain analy" 6 φορές. Αν είναι 5, δεν θα λειτουργήσει.

Και υπάρχει μια τρίτη επιλογή - γράψτε μια επιστολή στους pgsql-hackers. Έγραψα, ωστόσο, δεν είναι ακόμη σαφές εάν αυτό είναι σφάλμα ή δυνατότητα.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

https://gist.github.com/vlsi/df08cbef370b2e86a5c1

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Αυτός είναι ο κανόνας του 6 εξηγήστε. Τώρα στις τρέχουσες εκδόσεις πρέπει να το κάνετε 6 φορές αν έχετε δεσμευμένες μεταβλητές. Εάν δεν έχετε δεσμευμένες μεταβλητές, αυτό κάνουμε. Και τελικά αυτό ακριβώς το αίτημα αποτυγχάνει. Δεν είναι κάτι δύσκολο.

Φαίνεται, πόσο είναι δυνατό; Ένα bug εδώ, ένα bug εκεί. Στην πραγματικότητα, το σφάλμα είναι παντού.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Ας ρίξουμε μια πιο προσεκτική ματιά. Για παράδειγμα, έχουμε δύο σχήματα. Σχήμα Α με πίνακα S και διάγραμμα Β με πίνακα S. Ερώτημα – επιλέξτε δεδομένα από έναν πίνακα. Τι θα έχουμε σε αυτή την περίπτωση; Θα έχουμε σφάλμα. Θα έχουμε όλα τα παραπάνω. Ο κανόνας είναι - ένα σφάλμα υπάρχει παντού, θα έχουμε όλα τα παραπάνω.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Τώρα το ερώτημα είναι: «Γιατί;» Φαίνεται ότι υπάρχει τεκμηρίωση ότι εάν έχουμε ένα σχήμα, τότε υπάρχει μια μεταβλητή "search_path" που μας λέει πού να αναζητήσουμε τον πίνακα. Φαίνεται ότι υπάρχει μια μεταβλητή.

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Ορισμός αναζήτησης_διαδρομής + δηλώσεων προετοιμασμένων από διακομιστή =
Το αποθηκευμένο σχέδιο δεν πρέπει να αλλάξει τον τύπο αποτελέσματος

Πώς να το αντιμετωπίσετε; Υπάρχει μια απλή συνταγή - μην το κάνετε. Δεν χρειάζεται να αλλάξετε τη διαδρομή αναζήτησης κατά την εκτέλεση της εφαρμογής. Εάν αλλάξετε, είναι καλύτερο να δημιουργήσετε μια νέα σύνδεση.

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

Και θα τονίσω ξανά - αυτό είναι κάτι που δεν είναι χαρακτηριστικό για την Java. Θα δούμε το ίδιο πράγμα στο PL/pgSQL ένα προς ένα. Εκεί όμως θα αναπαραχθεί.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Ας δοκιμάσουμε περισσότερες επιλογές δεδομένων. Επιλέγουμε και επιλέγουμε. Έχουμε έναν πίνακα με ένα εκατομμύριο σειρές. Κάθε γραμμή είναι ένα kilobyte. Περίπου ένα gigabyte δεδομένων. Και έχουμε μια λειτουργική μνήμη στη μηχανή Java 128 megabyte.

Εμείς, όπως συνιστάται σε όλα τα βιβλία, χρησιμοποιούμε την επεξεργασία ροής. Ανοίγουμε δηλαδή το resultSet και διαβάζουμε τα δεδομένα από εκεί σιγά σιγά. Θα λειτουργήσει; Θα πέσει από τη μνήμη; Θα διαβάσεις λίγο; Ας εμπιστευτούμε τη βάση δεδομένων, ας εμπιστευτούμε την Postgres. Δεν το πιστεύουμε. Θα πέσουμε έξω από τη μνήμη; Ποιος βίωσε το OutOfMemory; Ποιος κατάφερε να το φτιάξει μετά; Κάποιος κατάφερε να το φτιάξει.

Εάν έχετε ένα εκατομμύριο σειρές, δεν μπορείτε απλώς να διαλέξετε. Απαιτείται OFFSET/LIMIT. Ποιος είναι για αυτήν την επιλογή; Και ποιος είναι υπέρ του να παίζεις με το autoCommit;

Εδώ, ως συνήθως, η πιο απροσδόκητη επιλογή αποδεικνύεται σωστή. Και αν απενεργοποιήσετε ξαφνικά το autoCommit, θα σας βοηθήσει. Γιατί αυτό? Η επιστήμη δεν ξέρει για αυτό.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Αλλά από προεπιλογή, όλοι οι πελάτες που συνδέονται σε μια βάση δεδομένων Postgres ανακτούν ολόκληρα τα δεδομένα. Το PgJDBC δεν αποτελεί εξαίρεση από αυτή την άποψη· επιλέγει όλες τις σειρές.

Υπάρχει μια παραλλαγή στο θέμα FetchSize, δηλαδή μπορείτε να πείτε σε επίπεδο ξεχωριστής δήλωσης ότι εδώ, επιλέξτε δεδομένα κατά 10, 50. Αλλά αυτό δεν λειτουργεί μέχρι να απενεργοποιήσετε την αυτόματη δέσμευση. Απενεργοποιήθηκε η αυτόματη δέσμευση - αρχίζει να λειτουργεί.

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Ας προχωρήσουμε στην εισαγωγή δεδομένων. Η εισαγωγή είναι ευκολότερη, υπάρχουν διαφορετικές επιλογές. Για παράδειγμα, INSERT, VALUES. Αυτή είναι μια καλή επιλογή. Μπορείτε να πείτε "INSERT SELECT". Στην πράξη είναι το ίδιο πράγμα. Δεν υπάρχει διαφορά στην απόδοση.

Τα βιβλία λένε ότι πρέπει να εκτελέσετε μια πρόταση Batch, τα βιβλία λένε ότι μπορείτε να εκτελέσετε πιο σύνθετες εντολές με πολλές παρενθέσεις. Και η Postgres έχει ένα υπέροχο χαρακτηριστικό - μπορείτε να κάνετε COPY, δηλαδή να το κάνετε πιο γρήγορα.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Αν το μετρήσετε, μπορείτε και πάλι να κάνετε μερικές ενδιαφέρουσες ανακαλύψεις. Πώς θέλουμε να λειτουργήσει αυτό; Θέλουμε να μην αναλύουμε και να μην εκτελούμε περιττές εντολές.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Στην πράξη, το TCP δεν μας επιτρέπει να το κάνουμε αυτό. Εάν ο πελάτης είναι απασχολημένος με την αποστολή ενός αιτήματος, τότε η βάση δεδομένων δεν διαβάζει τα αιτήματα σε προσπάθειες να μας στείλει απαντήσεις. Το τελικό αποτέλεσμα είναι ότι ο πελάτης περιμένει τη βάση δεδομένων να διαβάσει το αίτημα και η βάση δεδομένων περιμένει τον πελάτη να διαβάσει την απάντηση.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Και επομένως ο πελάτης αναγκάζεται να στέλνει περιοδικά ένα πακέτο συγχρονισμού. Επιπλέον αλληλεπιδράσεις δικτύου, επιπλέον χάσιμο χρόνου.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ ΣίτνικοφΚαι όσο τα προσθέτουμε τόσο χειρότερο γίνεται. Ο οδηγός είναι αρκετά απαισιόδοξος και τα προσθέτει αρκετά συχνά, περίπου μία φορά στις 200 γραμμές, ανάλογα με το μέγεθος των γραμμών κ.λπ.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

https://github.com/pgjdbc/pgjdbc/pull/380

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Πλεξούδα microbenchmark Java

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Ας το δοκιμάσουμε. Μετράμε το InsertBatch απλά. Μετράμε το InsertBatch πολλές φορές, δηλαδή το ίδιο πράγμα, αλλά υπάρχουν πολλές τιμές. Δύσκολη κίνηση. Δεν μπορούν όλοι να το κάνουν αυτό, αλλά είναι μια τόσο απλή κίνηση, πολύ πιο εύκολη από την ΑΝΤΙΓΡΑΦΗ.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Μπορείτε να κάνετε COPY.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Εάν ανοίξετε τον σύνδεσμο: pgjdbc/ubenchmsrk/InsertBatch.java, τότε αυτός ο κώδικας βρίσκεται στο GitHub. Μπορείτε να δείτε συγκεκριμένα ποια αιτήματα δημιουργούνται εκεί. Δεν πειράζει.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

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

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Εισάγουμε δεδομένα. Είναι ένα πολύ απλό τραπέζι. Τρεις στήλες. Και τι βλέπουμε εδώ; Βλέπουμε ότι και οι τρεις αυτές επιλογές είναι κατά προσέγγιση συγκρίσιμες. Και το COPY είναι, φυσικά, καλύτερο.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Αυτό είναι όταν εισάγουμε κομμάτια. Όταν λέγαμε ότι μία τιμή VALUES, δύο τιμές VALUES, τρεις τιμές VALUES ή υποδεικνύαμε 10 από αυτές χωρισμένες με κόμμα. Αυτό είναι απλώς οριζόντιο τώρα. 1, 2, 4, 128. Φαίνεται ότι το ένθετο παρτίδας, το οποίο είναι σχεδιασμένο με μπλε χρώμα, τον κάνει να αισθάνεται πολύ καλύτερα. Δηλαδή, όταν εισάγετε ένα κάθε φορά ή ακόμα και όταν εισάγετε τέσσερα τη φορά, γίνεται δύο φορές καλύτερο, απλά επειδή στριμώξαμε λίγο περισσότερο στις ΑΞΙΕΣ. Λιγότερες λειτουργίες EXECUTE.

Η χρήση του COPY σε μικρούς τόμους είναι εξαιρετικά απρόβλεπτη. Δεν ζωγράφισα καν στα δύο πρώτα. Πάνε στον παράδεισο, δηλαδή αυτοί οι πράσινοι αριθμοί για ΑΝΤΙΓΡΑΦΗ.

Το COPY θα πρέπει να χρησιμοποιείται όταν έχετε τουλάχιστον εκατό σειρές δεδομένων. Το γενικό κόστος ανοίγματος αυτής της σύνδεσης είναι μεγάλο. Και, για να είμαι ειλικρινής, δεν έσκαψα προς αυτή την κατεύθυνση. Βελτιστοποίησα το Batch, αλλά όχι το COPY.

Τι κανουμε μετα? Το δοκιμάσαμε. Καταλαβαίνουμε ότι πρέπει να χρησιμοποιήσουμε είτε δομές είτε ένα έξυπνο μπακ που συνδυάζει πολλές έννοιες.

Το PostgreSQL και το JDBC βγάζουν όλο το ζουμί. Βλαντιμίρ Σίτνικοφ

Τι πρέπει να αφαιρέσετε από τη σημερινή αναφορά;

  • Το PreparedStatement είναι το παν μας. Αυτό δίνει πολλά για την παραγωγικότητα. Παράγει μια μεγάλη πτώση στην αλοιφή.
  • Και πρέπει να κάνετε EXPLAIN ANALYZE 6 φορές.
  • Και πρέπει να αραιώσουμε το OFFSET 0 και κόλπα όπως το +0 για να διορθώσουμε το υπόλοιπο ποσοστό των προβληματικών μας ερωτημάτων.

Πηγή: www.habr.com

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