Τι είναι το MVCC και το Vacuum στη βάση δεδομένων της PostgreSQL και πώς αποφεύγουμε το Bloat

- Τι είναι το MVCC και το Vacuum στη βάση δεδομένων της PostgreSQL και πώς αποφεύγουμε το Bloat - 29 Ιούλιος 2026
- Πώς στέλνουμε email μέσα από Oracle Database όταν αποτύχει ένα RDBMS_SCHEDULER Job - 22 Απρίλιος 2026
- Πώς μεταφέρουμε CLOB πεδία κειμένου από Oracle Database σε SQL Server χωρίς προβλήματα - 19 Ιανουάριος 2026
Σε αυτό το άρθρο θα δούμε πώς η PostgreSQL διαχειρίζεται τη ταυτόχρονη πρόσβαση μέσω του MVCC, τι είναι το Bloat, τι είναι το Data Churn, τι είναι το Vacuum και πώς το ρυθμίζουμε σωστά με το Autovacuum.
Τι είναι το MVCC (Multi-Version Concurrency Control)
Το MVCC είναι ο μηχανισμός που επιτρέπει σε μια βάση δεδομένων να εξυπηρετεί ταυτόχρονα πολλούς χρήστες χωρίς να μπλοκάρονται τα reads (SELECT) από τα writes (INSERT/UPDATE/DELETE) και το αντίστροφο.
Όταν εκτελείται ένα UPDATE ή DELETE στην PostgreSQL, η παλιά εγγραφή δεν διαγράφεται αμέσως ούτε επανεγγράφεται στη μνήμη ή στον δίσκο. Αντίθετα, μαρκάρεται ως dead tuple (παλιά έκδοση) και παράλληλα δημιουργείται μια νέα έκδοση της εγγραφής για την τρέχουσα κατάσταση της βάσης δεδομένων.
Τι είναι το Data Churn και πώς δημιουργείται το Table Bloat
Όταν μιλάμε για Data Churn, αναφερόμαστε στη συχνότητα με την οποία εκτελούνται UPDATE και DELETE σε έναν πίνακα. Πίνακες που δέχονται συνεχείς αλλαγές έχουν υψηλό churn. Επειδή η PostgreSQL λόγω του MVCC δεν υπεργράφει τα δεδομένα αλλά δημιουργεί νέες εκδόσεις, το υψηλό churn παράγει ασταμάτητα dead tuples.
Το Table / Index Bloat είναι το άμεσο αποτέλεσμα αυτού του φαινομένου. Εμφανίζεται όταν ο φυσικός χώρος που καταλαμβάνει ένας πίνακας ή ένα ευρετήριο στο δίσκο είναι πολύ μεγαλύτερος από τον χώρο που καταλαμβάνουν τα πραγματικά, ενεργά δεδομένα. Αν το Autovacuum δεν προλαβαίνει να καθαρίσει αυτά τα dead tuples, η βάση δεδομένων γεμίζει με άδειο χώρο, αναγκάζοντας τα queries να διαβάζουν πολύ περισσότερα data pages και να καθυστερούν χωρίς λόγο.
Σύγκριση MVCC: PostgreSQL vs Oracle vs SQL Server
Παρόλο που και οι τρεις βάσεις δεδομένων επιτυγχάνουν απομονωμένα reads χωρίς κλειδώματα, η εσωτερική τους υλοποίηση διαφέρει σημαντικά:
- Oracle (UNDO Tablespace): Οι παλιές εκδόσεις των δεδομένων γράφονται στο Undo Segment. Ο πίνακας περιέχει πάντα μόνο την τρέχουσα έκδοση των δεδομένων.
- SQL Server (Snapshot Isolation): Χρησιμοποιεί το Row Versioning, όπου οι παλιές εκδόσεις των γραμμών διατηρούνται στην
tempdb(Version Store). - PostgreSQL (In-Place Versioning): Δεν διαθέτει ξεχωριστό UNDO tablespace. Όλες οι παλιές εκδόσεις των γραμμών (dead tuples) αποθηκεύονται μέσα στα ίδια τα data files του πίνακα. Αυτό δημιουργεί table & index bloat, καθιστώντας τη διαδικασία του Vacuum απόλυτα υποχρεωτική.
Πώς βρίσκουμε τους πίνακες που χρειάζονται καθάρισμα
Για να εντοπίσουμε ποιους πίνακες πρέπει να καθαρίσουμε, χρησιμοποιούμε το view pg_stat_user_tables.
Το παρακάτω query επιστρέφει το ποσοστό των dead tuples και παράγει αυτόματα έτοιμο το query του VACUUM ANALYZE για άμεση εκτέλεση:
SELECT
schemaname,
relname AS table_name,
n_live_tup AS live_tuples,
n_dead_tup AS dead_tuples,
-- Calculate dead tuples percentage over total rows
CASE
WHEN (n_live_tup + n_dead_tup) > 0
THEN ROUND(100.0 * n_dead_tup / (n_live_tup + n_dead_tup), 2)
ELSE 0
END AS dead_tuple_percent,
last_vacuum,
last_autovacuum,
-- Ready-to-use Vacuum Analyze command for immediate execution
format('VACUUM ANALYZE %I.%I;', schemaname, relname) AS vacuum_cmd
FROM pg_stat_user_tables
WHERE n_dead_tup >= 0 -- Filter tables with dead tuples (change to > 1000 for stricter check)
ORDER BY dead_tuples DESC;

To query επιστρέφει τα παρακάτω πεδία με το καθένα να φέρνει μια χρήσιμη πληροφορία:
schemaname: Το schema στο οποίο ανήκει ο πίνακας (π.χ.public).table_name: Το όνομα του πίνακα.live_tuples(n_live_tup): Ο αριθμός των ενεργών/ζωντανών εγγραφών.dead_tuples(n_dead_tup): Ο αριθμός των νεκρών εγγραφών που περιμένουν καθαρισμό.dead_tuple_percent: Το ποσοστό των νεκρών εγγραφών επί του συνόλου του πίνακα (δείκτης bloat).last_vacuum: Ημερομηνία/ώρα που εκτελέστηκε τελευταία φορά χειροκίνητοVACUUM.last_autovacuum: Ημερομηνία/ώρα που εκτελέστηκε τελευταία φορά το αυτόματοautovacuum.vacuum_cmd: Έτοιμη εντολή SQL που παράγεται δυναμικά για να τρέξετε απευθείαςVACUUM ANALYZEστον συγκεκριμένο πίνακα.
Autovacuum vs Manual Vacuum
Ο καθαρισμός στην PostgreSQL μπορεί να γίνει είτε αυτόματα είτε χειροκίνητα:
Autovacuum είναι ένας μηχανισμός που τρέχει συνεχώς, παρακολουθεί τα στατιστικά των πινάκων και μόλις εντοπίσει ότι ο αριθμός των dead tuples ξεπέρασε ένα συγκεκριμένο όριο, ξεκινάει μόνος του τη διαδικασία καθαρισμού.
Από την άλλη, το Manual Vacuum (VACUUM ANALYZE) το τρέχουμε εμείς τακτικά ή μετά από μαζικά DELETE/UPDATE. Ελευθερώνει τον χώρο των dead tuples εντός των data pages του πίνακα ώστε να χρησιμοποιηθεί από μελλοντικά INSERT/UPDATE και παράλληλα ενημερώνει τα στατιστικά του query planner. Αξίζει να σημειωθεί ότι το απλό Vacuum δεν επιστρέφει τον χώρο στο λειτουργικό σύστημα για αυτό απαιτείται VACUUM FULL, το οποίο όμως παίρνει αποκλειστικό lock στον πίνακα.
Για παράδειγμα, για χειροκίνητο καθαρισμό τρέχουμε:
VACUUM ANALYZE public.my_table;
Tuning και Best Practices για το Autovacuum
Τα default settings του Autovacuum στην PostgreSQL είναι σχεδιασμένα για μικρά περιβάλλοντα. Σε παραγωγικές βάσεις με υψηλό workload, απαιτείται tuning είτε σε επίπεδο βάσης δεδομένων (postgresql.conf) είτε σε επίπεδο πίνακα:
Η πιο σημαντική παράμετρος είναι το autovacuum_vacuum_scale_factor (default: 0.20). Ορίζει ότι πρέπει να αλλάξει το 20% των εγγραφών ενός πίνακα για να ενεργοποιηθεί το autovacuum. Σε μεγάλους πίνακες, το 20% είναι τεράστιο νούμερο. Γι’ αυτό, η ενδεδειγμένη τακτική είναι να το κατεβάσουμε στο 0.05 (5%) καθολικά στη βάση ή τοπικά στους πίνακες.
Παράλληλα, η παράμετρος autovacuum_max_workers (default: 3) καθορίζει πόσα παράλληλα processes μπορούν να κάνουν vacuum ταυτόχρονα σε διαφορετικούς πίνακες. Σε servers με πολλά CPU cores, καλό είναι να το αυξάνουμε σε 6 ή 8.
Τέλος, το autovacuum_vacuum_cost_limit (default: 200) περιορίζει το I/O που καταναλώνει το autovacuum για να μην επηρεάζει την εφαρμογή. Σε γρήγορους δίσκους, η τιμή 200 είναι πολύ συντηρητική και καθυστερεί τους καθαρισμούς. Μπορούμε να το αυξήσουμε σε 1000 ή 2000 ώστε οι καθαρισμοί να ολοκληρώνονται πολύ γρήγορα.
Για παράδειγμα αν θέλουμε να αλλάξουμε το scale factor καθολικά σε επίπεδο βάσης δεδομένων αλλάζουμε στο postgresql.conf την παρακάτω παράμετρο:
autovacuum_vacuum_scale_factor = 0.05
Αν όμως θέλουμε να το αλλάξουμε μόνο σε συγκεκριμένο πίνακα τρέχουμε το εξής:
ALTER TABLE public.table SET (autovacuum_vacuum_scale_factor = 0.05);

