VACUUM FULL vai pg_repack ir izvēle, ar ko saskaraties, kad PostgreSQL tabula izaugusi krietni lielāka par tajā esošajiem datiem un jums vajag atgūt diska vietu. Abi pārraksta tabulu kompaktā jaunā failā. VACUUM FULL ir iebūvēts, bet bloķē tabulu visā pārrakstīšanas laikā. pg_repack ir paplašinājums, kas dara to pašu, lielāko daļu laika atstājot tabulu pieejamu lasīšanai un rakstīšanai. Un PostgreSQL 19, kas pašlaik ir laidiena kandidāta stadijā, pievieno iebūvētu REPACK (CONCURRENTLY), kas strādā pēc tā paša principa.
Vispirms: vai pārrakstīšana tiešām vajadzīga?
VACUUM dokumentācija skaidri apraksta atšķirību. Parasts VACUUM atgūst vietu un padara to pieejamu atkārtotai izmantošanai tajā pašā tabulā bez ekskluzīvas slēdzenes, bet vairumā gadījumu neatdod to operētājsistēmai. Izņēmums, saskaņā ar nodaļu par regulāro vakuumēšanu, ir tad, kad lapas pašās tabulas beigās kļūst pilnīgi tukšas un ekskluzīvu slēdzeni var viegli iegūt: tad šīs lapas tiek nogrieztas.
Tātad tabula, kas ir 60% tukša, nav automātiski problēma. Ja tā atkal piepildīsies ar jaunām rindām, atkārtoti izmantojama vieta ir kārtībā. Tajā pašā nodaļā teikts, ka nav lielas jēgas saspiest tabulu, kas vienkārši atkal izaugs, un ka mēreni bieži standarta VACUUM ir labāki par retiem VACUUM FULL. Pārrakstīšana ir jēgpilna, ja:
- jūs vienreiz izdzēsāt vai atjauninājāt lielāko daļu tabulas (arhīva tīrīšana, neveiksmīga datu aizpildīšana) un tā vairs neaugs;
- diskā tiešām beidzas vieta, un brīvā vieta tabulas iekšienē ir ātrākais, ko atgūt;
- secīgā skenēšana tabulai ir lēna, jo tā lasa galvenokārt tukšas lapas.
Ja bloat atgriežas atkal un atkal, pārrakstīšana tikai nopērk laiku. Cēlonis gandrīz vienmēr ir autovacuum, kas netiek līdzi, vai gara transakcija, kas aiztur tīrīšanu; to aplūkojam autovacuum regulēšanas rakstā.
Izmēriet, pirms izlemjat
Sāciet ar izmēriem un mirušo rindu skaitu no statistikas skatiem:
SELECT relname,
pg_size_pretty(pg_table_size(relid)) AS table_size,
pg_size_pretty(pg_indexes_size(relid)) AS indexes_size,
n_live_tup, n_dead_tup, last_autovacuum
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 20;
n_dead_tup ir aplēse un neko nepasaka par brīvo vietu, ko atstājušas rindas, kuras vacuum jau iztīrījis. Reālam skaitlim izmantojiet paplašinājumu pgstattuple. pgstattuple_approx ir dokumentēta ātrāka pgstattuple alternatīva, kas atgriež aptuvenus rezultātus:
CREATE EXTENSION IF NOT EXISTS pgstattuple;
SELECT table_len, approx_tuple_percent, dead_tuple_percent, approx_free_percent
FROM pgstattuple_approx('public.orders'::regclass);
Augsts approx_free_percent lielai tabulai, kas vairs neaugs, ir jūsu kandidāts. Pēc noklusējuma šīs funkcijas var izpildīt tikai superlietotāji un pg_stat_scan_tables dalībnieki. Pat aptuvenā versija nolasa daļu tabulas, tāpēc ļoti lielām tabulām palaidiet to ārpus noslodzes stundām.
VACUUM FULL: vienkāršs, iebūvēts un bloķējošs
Saskaņā ar dokumentāciju VACUUM FULL pārraksta visu tabulas saturu jaunā diska failā bez liekas vietas, ļaujot neizmantoto vietu atdot operētājsistēmai. Cena:
- ACCESS EXCLUSIVE slēdzene visā darbības laikā. Šī slēdzene konfliktē ar visu, ieskaitot parastu
SELECT. Lielai tabulai tas nozīmē minūtes vai stundas, kamēr lietotne gaida. - Papildu diska vieta. Jaunā kopija tiek uzrakstīta, pirms vecā tiek atbrīvota, tāpēc uz laiku vajag vēl aptuveni tabulas izmēru, plus tās indeksus, kas arī tiek pārbūvēti. Tas ir klasiskais slazds: palaist
VACUUM FULL, jo disks ir gandrīz pilns, un pusceļā palikt bez vietas. - Nav paralēlisma. Opciju
PARALLELnevar apvienot arFULL.
VACUUM (FULL, VERBOSE, ANALYZE) public.orders;
-- citā sesijā
SELECT pid, command, phase, heap_blks_scanned, heap_blks_total
FROM pg_stat_progress_cluster;
Dokumentācijā norādīts, ka procesi, kas izpilda VACUUM FULL, ziņo par progresu skatā pg_stat_progress_cluster, nevis pg_stat_progress_vacuum. Starp citu, autovacuum nekad neizpilda VACUUM FULL.
VACUUM FULL ir pareizais rīks mazām tabulām, kur īsa slēdzene nav svarīga, apkopes logiem un gadījumiem, kad nevarat instalēt paplašinājumus. Visam lielam un noslogotam skatieties tiešsaistes variantus.
pg_repack: pārrakstīšana tiešsaistē
pg_repack ir atvērtā koda paplašinājums, kas novērš bloat tabulās un indeksos. Tā dokumentācijā norādīts atbalsts PostgreSQL 9.5 un jaunākām versijām, ieskaitot 18. Tas var veikt tiešsaistes CLUSTER (rindas sakārtotas pēc indeksa) vai ar --no-order tiešsaistes VACUUM FULL. Pilna tabulas pārpakošana notiek tā:
- Izveido žurnāla tabulu un trigeri, kas ieraksta katru
INSERT,UPDATEunDELETEsākotnējā tabulā. - Izveido jaunu tabulu ar visām vecās tabulas rindām un uzbūvē tās indeksus.
- Piemēro žurnāla tabulā uzkrātās izmaiņas.
- Sistēmas katalogos apmaina tabulas, ieskaitot indeksus un TOAST, un dzēš sākotnējo.
ACCESS EXCLUSIVE slēdzene tiek turēta tikai īsu brīdi sagatavošanas laikā un beigu apmaiņas un dzēšanas laikā. Pārējā laikā pg_repack tur SHARE UPDATE EXCLUSIVE slēdzeni (kopš versijas 1.5.3; agrākās versijas izmantoja ACCESS SHARE), tāpēc parasta lasīšana un rakstīšana turpinās.
-- vienreiz katrā datubāzē
CREATE EXTENSION pg_repack;
# apskatīt, kas tiktu apstrādāts, neko nedarot
pg_repack --dry-run --no-order --table=public.orders mydb
# tiešsaistes "VACUUM FULL" vienai tabulai; padoties, nevis atcelt citus vaicājumus
pg_repack --no-order --table=public.orders --wait-timeout=30 --no-kill-backend mydb
Kas pg_repack vajadzīgs
- Primārā atslēga vai vismaz unikāls indekss uz
NOT NULLkolonnas. Tabulas bez tās nevar pārpakot. - Diska vieta aptuveni divreiz lielāka par mērķa tabulām un to indeksiem, saskaņā ar tā dokumentāciju. Tas ir vairāk, nekā vajag
VACUUM FULL, jo ir žurnāla tabula un kopija. - Superlietotājs vai tabulas īpašnieks. Īpašniekiem jānorāda
--no-superuser-check. - Saskaņotas versijas
pg_repackklienta programmai un datubāzē instalētajam paplašinājumam; neatbilstība ir bieža pirmā kļūda.
Kur tas var sāpēt
- Īsās slēdzenes tomēr ir jāiegūst. Ja garš vaicājums tur konfliktējošu slēdzeni, pg_repack gaida
--wait-timeoutsekundes (noklusējums 60), tad atceļ konfliktējošos vaicājumus, un pēc divkārša taimauta pārtrauc atlikušos procesus. Produkcijā mēs dodam priekšroku--no-kill-backend, kas tā vietā izlaiž tabulu, un palaišanai klusā laikā. - Darbības laikā nekāda DDL tabulai. Dokumentācijā teikts, ka pilnas tabulas pārpakošanas laikā DDL ir bloķēts.
- Rakstīšanas papildu slodze. Katra izmaiņa darbības laikā iet caur trigeri žurnāla tabulā. Ļoti intensīvi rakstītai tabulai tas palielina slodzi, un beigu izlīdzināšana var aizņemt laiku.
- Atliekas pēc pārtraukuma. Ja darbība tiek pārtraukta, tā var atstāt
repack_triggervai pagaidu indeksu. Dokumentācijā paskaidrots, kā tos noņemt; pārbaudiet tos pirms nākamā mēģinājuma.
Ja uzpūsti ir tikai indeksi, tabulas pārrakstīšana nemaz nav vajadzīga. pg_repack ir --only-indexes, un kopš PostgreSQL 12 iebūvētais REINDEX CONCURRENTLY to paveic bez paplašinājuma.
PostgreSQL 19: REPACK un REPACK (CONCURRENTLY)
PostgreSQL 19 laidiena piezīmes pievieno komandu REPACK, kas aizstāj VACUUM FULL un CLUSTER; vecās komandas saglabātas saderībai, un PostgreSQL 19 VACUUM lapā opcija FULL atzīmēta kā novecojusi. Vēl svarīgāk, REPACK (CONCURRENTLY) pārbūvē tabulu, neturot access-exclusive slēdzeni visas operācijas laikā. Šodien PostgreSQL 19 ir laidiena kandidāta stadijā (RC1 tika izziņots 2026. gada 15. oktobrī), tāpēc uztveriet to kā to, kas nāks, nevis to, ko darbināt tagad.
-- PostgreSQL 19
REPACK (CONCURRENTLY, VERBOSE, ANALYZE) public.orders;
SELECT * FROM pg_stat_progress_repack;
Saskaņā ar REPACK dokumentāciju vienlaicīgais režīms lielāko darba daļu veic ar SHARE UPDATE EXCLUSIVE, vienlaicīgās izmaiņas uztver ar loģisko dekodēšanu un ACCESS EXCLUSIVE ņem tikai beigu apmaiņai. Dokumentētos ierobežojumus vērts izlasīt, pirms to plānojat:
- Tas nav MVCC-drošs: pēc tā apstiprināšanas transakcijas, kas izmanto momentuzņēmumu, kas uzņemts pirms pārpakošanas sākuma, redzēs tabulu kā tukšu.
- To nevar izmantot nežurnalētām (unlogged) vai sadalītām tabulām, tabulām bez primārās atslēgas vai uz indeksa balstītas replica identity, sistēmas katalogiem, transakcijas blokā vai tad, ja
max_repack_replication_slots(noklusējums 5, iestatāms servera startā) nav brīva slota. - Tas neizdodas, ja darbības laikā vienlaikus tiek atjaunināti vai dzēsti vairāk nekā aptuveni 104 miljoni rindu, un var neizdoties, ja citas transakcijas tabulai izpilda DDL.
- Transakciju ID pārpildes (wraparound) nozīmē visa darbība skaitās kā viena gara transakcija, tāpēc ļoti lielām tabulām tā aiztur tīrīšanu citur; dokumentācija iesaka sekot
pg_stat_activity.backend_xid.
Ko izmantot
- Maza tabula vai apkopes logs:
VACUUM FULL. Nekas nav jāinstalē, nekas nav jātīra. - Liela, noslogota tabula PostgreSQL 18 vai vecākā: pg_repack ar
--no-order, vispirms sausais palaidiens,--no-kill-backendun pietiekami daudz brīva diska. - PostgreSQL 19, kad būsiet uz tā: notestējiet
REPACK (CONCURRENTLY)ar savu slodzi, īpaši MVCC brīdinājumu ilgstošām atskaitēm. - Uzpūsti tikai indeksi:
REINDEX CONCURRENTLY, tabulas pārrakstīšana nav vajadzīga. - Bloat atgriežas atkal un atkal: neviens no iepriekšminētajiem. Vispirms salabojiet autovacuum un garās transakcijas.
Galvenās atziņas
- Parasts
VACUUMpadara vietu atkārtoti izmantojamu, bet reti atdod to OS; tas ir normāli un bieži vien pietiekami. - Pirms kaut ko pārrakstāt, izmēriet ar
pgstattuple_approxun pārrakstiet tikai tabulas, kas vairs neaugs. VACUUM FULLturACCESS EXCLUSIVEslēdzeni visā darbības laikā un prasa papildu disku jaunajai kopijai.- pg_repack uztur tabulu tiešsaistē ar īsām slēdzenēm, bet prasa primāro atslēgu vai unikālu NOT NULL indeksu, brīvu disku aptuveni divreiz tabulas un indeksu izmērā un plānu slēdzeņu taimautiem.
- PostgreSQL 19 pievieno
REPACK (CONCURRENTLY)ar dokumentētiem ierobežojumiem, tostarp to, ka tas nav MVCC-drošs.