PostgreSQL bruger ikke IN-PLACE opdateringsmekanisme, så i henhold til den måde DELETE og UPDATE er designet på,
- Når der udføres DELETE-handlinger, markerer det den eksisterende tuple som DØD i stedet for fysisk at fjerne disse tuples.
- Tilsvarende, når UPDATE-operationen udføres, markerer den den tilsvarende eksisterende tuple som DØD og indsætter en ny tuple (dvs. UPDATE-operation =DELETE + INSERT).
Så hver DELETE og UPDATE kommando vil resultere i en DEAD tuple, som aldrig vil blive brugt (medmindre der er parallelle transaktioner). Disse døde tupler vil føre til unødvendigt ekstra pladsforbrug, selvom det samme eller færre antal effektive poster. Dette kaldes også space bloating i PostgreSQL. Da PostgreSQL er meget udbredt som OLTP slags relationsdatabasesystem, hvor der ofte udføres INSERT, UPDATE og DELETE operationer, vil der være mange DEAD tupler og dermed tilsvarende konsekvenser. Så PostgreSQL krævede en stærk vedligeholdelsesmekanisme for at håndtere disse DEAD-tupler. VACUUM er vedligeholdelsesprocessen, der tager sig af håndteringen af DEAD tuple sammen med et par flere aktiviteter, der er nyttige til at optimere VACUUM-driften. Lad os forstå noget terminologi, der skal bruges senere i denne blog.
Synlighedskort
Som navnet antyder, vedligeholder det synlighedsoplysninger om sider, der kun indeholder tuples, der vides at være synlige for alle aktive transaktioner. For hver side bruges en bit. Hvis bit er sat til 1, betyder det, at alle tupler på den tilsvarende side er synlige. Bittet sat til 0 betyder, at der ikke er ledig plads på den givne side, og tuples kan være synlige for alle transaktioner.
Synlighedskort vedligeholdes for hver relation (tabel og indeks) og associeres sammen med hovedrelationer, dvs. hvis relationsfilens nodenavn er 12345, så gemmes synlighedsfilen i parallelfilen 12345_vm.
Fripladskort
Den vedligeholder information om ledig plads, der indeholder detaljer om den tilgængelige plads i relationen. Dette er også gemt i filen parallelt med relationens hovedfil, dvs. hvis relationsfilens nodenavn er 12345, så gemmes den ledige pladskortfil i parallelfilen 12345_fsm.
Frys Tuple
PostgreSQL bruger 4 bytes til lagring af transaktions-id, hvilket betyder, at der maksimalt kan genereres 2 milliarder transaktioner, før det omsluttes. Overvej nu stadigvæk på dette tidspunkt, at nogle tuple indeholder indledende transaktions-id, f.eks. 100, så for den nye transaktion (som bruger den omsluttede transaktion), siger 5, transaktions-id 100 vil se ind i fremtiden, og den vil ikke være i stand til at se de tilføjede data /modificeret af det, selvom det faktisk var i fortiden. For at undgå denne særlige transaktions-id tildeles FrozenTransactionId (lig med 2). Dette særlige transaktions-id anses altid for at være i fortiden og vil være synligt for alle transaktioner.
VAKUUM
VACUUMs primære opgave er at genvinde lagerplads optaget af DEAD-tupler. Genvundet lagerplads gives ikke tilbage til operativsystemet, men defragmenteres blot på den samme side, så de er blot tilgængelige til genbrug ved fremtidig dataindsættelse i den samme tabel. Mens VAKUUM-operation foregår på et bestemt bord, kan andre LÆSE/SKRIVE-operationer samtidigt udføres på samme bord, da eksklusiv lås ikke tages på det pågældende bord. Hvis der ikke er angivet et tabelnavn, udføres VACUUM på alle tabeller i databasen. VACUUM-operationen udføres under en række operationer i en ShareUpdateExclusive-lås:
- Scan alle sider i alle tabeller (eller specificerede tabel) i databasen for at få alle døde tupler.
- Frys gamle tupler, hvis det kræves.
- Fjern indekstuplen, der peger på de respektive DEAD-tupler.
- Fjern DEAD-tupler fra en side, der svarer til en specifik tabel, og omfordel de levende tupler på siden.
- Opdater ledigt kort (FSM) og Visibility Map (VM).
- Trunker den sidste side, hvis det er muligt (hvis der var DØDE tupler, der blev befriet).
- Opdater alle tilsvarende systemtabeller.
Som vi kan se fra ovenstående arbejdstrin for VACUUM, er det klart, at det er en meget bekostelig operation, da den skal behandle alle sider af relationen. Så det er i høj grad nødvendigt at springe mulige sider over, som ikke kræver at blive støvsuget. Da Visibility map (VM) giver information om siden, hvor hvis der ikke er ledig plads, kan det antages, at det tilsvarende sidevakuum ikke er påkrævet, og derfor kan denne side sikkert springes over.
Da VACUUM alligevel gennemgår alle sider og deres alle tupler, så det benytter lejligheden til at udføre en anden vigtig opgave med at fryse de kvalificerende tuples.
Fuld VAKUUM
Som diskuteret i det foregående afsnit, selvom VACUUM fjerner alle DEAD-tupler og defragmenterer siden til fremtidig brug, hjælper det ikke med at reducere den samlede lagring af tabellen, da pladsen faktisk ikke frigives til operativ system. Antag, at en tabel tbl1, at det samlede lager har nået 1,5 GB og ud af dette er 1 GB optaget af død tuple, så efter VACUUM vil yderligere ca. 1 GB være tilgængelig for yderligere tuple indsættelse, men stadig vil den samlede lagerplads forblive som 1,5 GB.
Fuldstøvsuger løser dette problem ved faktisk at frigøre plads og returnere det tilbage til operativsystemet. Men dette kommer til at koste. I modsætning til VACUUM tillader FULD VACUUM ikke parallel drift, da det kræver en eksklusiv lås på forholdet, der bliver FULD VACUUM. Nedenfor er trinene:
- Låser eksklusivt på relationen.
- Opret en parallel tom lagerfil.
- Kopiér alle live-tupler fra nuværende lager til nyligt tildelt lager.
- Giv derefter den originale lagerplads fri.
- Løs låsen.
Så som det også fremgår af trinene, vil den kun have lagerplads påkrævet for de resterende data.
Automatisk VAKUUM
I stedet for at udføre VACUUM manuelt, understøtter PostgreSQL en dæmon, som automatisk udløser VACUUM med jævne mellemrum. Hver gang VACUUM vågner op (som standard 1 minut), kalder det flere værker (afhængigt af konfigurationen af autovacuum_worker processer).
Autostøvsugere udfører VAKUUM-processer samtidigt for de respektive udpegede tabeller. Da VACUUM ikke tager nogen eksklusiv lås på tabeller, påvirker det ikke (eller minimalt) andet databasearbejde.
Konfigurationen af Auto-VACUUM skal udføres baseret på databasens brugsmønster. Det bør ikke være for hyppigt (da det vil spilde, at arbejderen vågner op, da der måske ikke er eller for få døde tupler) eller for meget forsinket (det vil forårsage en masse døde tupler sammen og dermed bordoppustethed).
VAKUUM eller Fuldt vakuum
Ideelt set bør databaseapplikationen designes på en måde, så der ikke er behov for FULDSTØMMELSE. Som forklaret ovenfor genskaber FULL VACUUM lagerplads og sætter dataene tilbage, så hvis der kun er færre døde tupler, vil der straks blive genskabt lagerplads for at lægge alle originale data tilbage. Også da FULL VACUUM tager eksklusiv lås på bordet, blokerer det alle operationer på det tilsvarende bord. Så ved at gøre FULD STØMME kan det nogle gange bremse den overordnede database.
Opsummering Fuld VACUUM bør undgås, medmindre det vides, at størstedelen af lagerpladsen skyldes døde tupler. PostgreSQL-udvidelsen pg_freespacemap kan bruges til at få et retvisende tip om ledig plads.
Lad os se et eksempel på den forklarede VACUUM-proces.
Lad os først oprette en tabeldemo1:
postgres=# create table demo1(id int, id2 int);
CREATE TABLE
Og indsæt nogle data der:
postgres=# insert into demo1 values(generate_series(1,10000), generate_series(1,
10000));
INSERT 0 10000
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
45 | 0.00
(1 row)
Lad os nu slette data:
postgres=# delete from demo1 where id%2=0;
DELETE 5000
Og kør en manuel vakuum:
postgres=# vacuum demo1;
VACUUM
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
45 | 45.07
(1 row)
Denne ledige plads er nu tilgængelig til at blive genbrugt af PostgreSQL, men hvis du vil frigive den plads til operativsystemet, skal du køre:
postgres=# vacuum full demo1;
VACUUM
postgres=# SELECT count(*) as npages, round(100 * avg(avail)/8192 ,2) as average_freespace_ratio FROM pg_freespace('demo1');
npages | average_freespace_ratio
--------+-------------------------
23 | 0.00
(1 row)
Konklusion
Og dette var et kort eksempel på, hvordan VACUUM-processen fungerer. Heldigvis, takket være den automatiske vakuumproces, det meste af tiden og i et almindeligt PostgreSQL-miljø, behøver du ikke tænke på dette, fordi det styres af selve motoren.