Πώς λειτουργεί η μνήμη στη PostgreSQL και ποιες παραμέτρους ρυθμίζουμε

Πώς λειτουργεί η μνήμη στη PostgreSQL και ποιες παραμέτρους ρυθμίζουμε
Πώς λειτουργεί η μνήμη στη PostgreSQL και ποιες παραμέτρους ρυθμίζουμε

Σε προηγούμενο άρθρο έχουμε αναλύσει την αρχιτεκτονική της Oracle Database (SGA/PGA). Αυτή την φορά θα δούμε την αρχιτεκτονική μνήμης της PostgreSQL και τις βασικές παραμέτρους για την βέλτιστη απόδοση.

Το μοντέλο μνήμη της PostgreSQL διαφέρει σημαντικά από αυτό του SQL Server ή της Oracle Database. Θα πρέπει οπωσδήποτε να γνωρίζουμε πως κατανέμεται η μνήμη ώστε να αποφύγουμε σφάλματα όπως το Out of Memory (OOM) στο λειτουργικό σύστημα.

Η Αρχιτεκτονική Μνήμης στην PostgreSQL

Η συνολική μνήμη RAM που δεσμεύει η PostgreSQL χωρίζεται σε δύο μεγάλες κατηγορίες:

Shared Memory Areas: Περιοχές μνήμης που μοιράζονται όλες οι διεργασίες (processes/sessions) της βάσης.

Local Memory Areas: Περιοχές μνήμης που δεσμεύονται ξεχωριστά από κάθε process για την εκτέλεση συγκεκριμένων queries.

Πώς λειτουργεί η μνήμη στη PostgreSQL και ποιες παραμέτρους ρυθμίζουμε

Shared Memory Parameters

Στην κοινόχρηστη μνήμη, η πιο κομβική παράμετρος είναι το shared_buffers, το οποίο αποτελεί την κύρια περιοχή cache για τα data pages και τα indexes (αντίστοιχο με το Buffer Pool του SQL Server ή το Buffer Cache στο SGA της Oracle). Η προεπιλεγμένη τιμή του είναι συνήθως 128MB. Στον SQL Server είμαστε συνηθισμένοι να δίνουμε το 80% της RAM στη βάση δεδομένων, όμως στην PostgreSQL αυτό είναι λάθος, καθώς η αρχιτεκτονική της βασίζεται σημαντικά στο Page Cache του ίδιου του Λειτουργικού Συστήματος. Γι’ αυτόν τον λόγο, η ενδεδειγμένη τιμή για το shared_buffers είναι το 25% της συνολικής RAM του server.

Ένα άλλο σημαντικό τμήμα της Shared Memory είναι ο Lock Manager, η ειδική περιοχή όπου αποθηκεύονται όλα τα κλειδώματα (row-level, table-level, advisory locks, lightweight locks) που κρατούν ή περιμένουν τα ενεργά transactions. Το μέγεθος της μνήμης που δεσμεύει ο Lock Manager υπολογίζεται δυναμικά με βάση τις παραμέτρους max_locks_per_transaction (default 64) και max_connections. Αν ένα query προσπαθήσει να αποκτήσει κλειδώματα σε πάρα πολλούς πίνακες ταυτόχρονα και εμφανιστεί το σφάλμα out of shared memory, η λύση είναι να αυξήσουμε την παράμετρο max_locks_per_transaction σε 128.

Τέλος, στην κοινόχρηστη μνήμη ανήκουν και τα wal_buffers, στα οποία αποθηκεύονται προσωρινά τα δεδομένα του Write-Ahead Logging (WAL) πριν εγγραφούν στο δίσκο, όπως κάνει το Log Buffer σε άλλα RDBMS. Η τιμή τους ρυθμίζεται αυτόματα στο 3% του shared_buffers (με ανώτατο όριο τα 16MB) και συνήθως δεν χρειάζεται να παρέμβουμε χειροκίνητα

Αυτές τις παραμέτρους αν τις αλλάξουμε στο postgresql.conf απαιτούν να γίνει restart το service, δεν ειναι δυναμικές παράμετροι.

Local Process Memory Parameters (Ανά Session/Query)

Εδώ κρύβεται η μεγαλύτερη παγίδα για out of memory crashes, καθώς οι παράμετροι αυτοί δεν αφορούν τη συνολική μνήμη του session, αλλά κάθε μεμονωμένο operation.

Το work_mem χρησιμοποιείται για εσωτερικές εργασίες ταξινόμησης (όπως ORDER BY και DISTINCT) καθώς και για joins. Η προεπιλεγμένη τιμή του είναι μόλις 4MB. Δεν ορίζεται μία φορά ανά σύνδεση, αλλά ανά operation μέσα στο πλάνο εκτέλεσης. Αυτό σημαίνει ότι αν ένα σύνθετο query εκτελεί 3 Hash Joins και 2 Sorts, μπορεί να καταναλώσει έως και 5 φορές τη μνήμη αυτή κατά την εκτέλεσή του. Για την αποφυγή OOM, διατηρούμε το work_mem χαμηλά σε επίπεδο βάσης π.χ. 32MB. Αν ένα συγκεκριμένο βαρύ report χρειάζεται περισσότερη μνήμη, το αλλάζουμε προσωρινά στο συγκεκριμένο session και στη συνέχεια το επαναφέρουμε:

SET work_mem = '256MB';

RESET work_mem;

Παράλληλα, για τα sessions που κάνουν χρήση προσωρινών πινάκων (TEMP TABLE), υπάρχει η παράμετρος temp_buffers. Καθορίζει το ανώτατο όριο μνήμης που μπορεί να χρησιμοποιήσει κάθε session για να κρατάει στη RAM τα data pages των temporary tables, με default τιμή τα 8MB. Αν ένα query παράγει μεγάλους προσωρινούς πίνακες που ξεπερνούν αυτό το όριο, η PostgreSQL θα αναγκαστεί να γράψει τα εναπομείναντα δεδομένα στο δίσκο, προκαλώντας I/O overhead. Σε περιβάλλοντα με βαριά ETL processes μπορούμε να αυξήσουμε το temp_buffers π.χ. στα 32MB είτε καθολικά είτε τοπικά στο session που εκτελεί τη διεργασία.

Από την αλλη, το maintenance_work_mem καθορίζει τη μνήμη που χρησιμοποιείται για εργασίες συντήρησης, όπως το CREATE INDEX, το ALTER TABLE ADD FOREIGN KEY και το VACUUM. Η προεπιλεγμένη τιμή του είναι 64MB, αλλά επειδή οι εργασίες συντήρησης εκτελούνται σπάνια και από μεμονωμένα processes, μπορούμε να δώσουμε αρκετά υψηλότερη τιμή π.χ. 2GB, ώστε τα index builds και οι καθαρισμοί να ολοκληρώνονται πολύ ταχύτερα.

Οι Local Parameters ειναι δυναμικές δεν χρειάζεται να πέσει το service αν τις αλλάξουμε στο postgresql.conf χρειάζεται απλά reload μέσω SQL (SELECT pg_reload_conf();)

Πώς ελέγχουμε τη χρήση της Shared Memory (Cache Hit Ratio)

Για να διαπιστώσουμε αν η μνήμη shared_buffers επαρκεί ή αν η βάση διαβάζει συνεχώς από το δίσκο, παρακολουθούμε το Buffer Cache Hit Ratio με το παρακάτω query:

SELECT 
    COALESCE(sum(heap_blks_read), 0) AS disk_reads,
    COALESCE(sum(heap_blks_hit), 0)  AS buffer_hits,
    CASE 
        WHEN (COALESCE(sum(heap_blks_hit), 0) + COALESCE(sum(heap_blks_read), 0)) > 0 
        THEN ROUND(
            100.0 * sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)), 
            2
        )
        ELSE 0 
    END AS cache_hit_ratio
FROM pg_statio_user_tables;
Πώς λειτουργεί η μνήμη στη PostgreSQL και ποιες παραμέτρους ρυθμίζουμε

Ερμηνεία του αποτελέσματος:

  • cache_hit_ratio > 99%: Η βάση εξυπηρετεί τα αιτήματα σχεδόν εξ ολοκλήρου από τη μνήμη RAM.
  • cache_hit_ratio < 95%: Αυξημένο I/O στο δίσκο. Απαιτείται έλεγχος για missing indexes ή πιθανή αύξηση του shared_buffers.
  • 0% (ή μηδενικά reads/hits): Η βάση μόλις ξεκίνησε ή δεν έχουν εκτελεστεί ακόμα queries στους πίνακες χρηστών.

Πηγές:

Μοιράσου το

Αφήστε μία απάντηση