PostgreSQL gebruikt geen IN-PLACE updatemechanisme, dus volgens de manier waarop de DELETE- en UPDATE-opdrachten zijn ontworpen,
- Telkens wanneer er DELETE-bewerkingen worden uitgevoerd, wordt de bestaande tuple gemarkeerd als DEAD in plaats van die tuples fysiek te verwijderen.
- Evenzo, telkens wanneer de UPDATE-bewerking wordt uitgevoerd, markeert het de corresponderende bestaande tuple als DEAD en voegt het een nieuwe tuple in (d.w.z. UPDATE-bewerking =DELETE + INSERT).
Dus elk DELETE- en UPDATE-commando resulteert in één DEAD-tuple, die nooit zal worden gebruikt (tenzij er parallelle transacties zijn). Deze dode tupels zullen leiden tot onnodig extra ruimtegebruik, hoewel hetzelfde of minder effectieve records. Dit wordt ook wel space bloating genoemd in PostgreSQL. Aangezien PostgreSQL veel wordt gebruikt als een relationeel databasesysteem van het OLTP-type, waar regelmatig INSERT-, UPDATE- en DELETE-bewerkingen worden uitgevoerd, zullen er veel DEAD-tupels zijn en dus bijbehorende gevolgen. PostgreSQL had dus een sterk onderhoudsmechanisme nodig om met deze DEAD-tupels om te gaan. VACUM is het onderhoudsproces dat zorgt voor het omgaan met DEAD tuple, samen met een paar andere activiteiten die nuttig zijn voor het optimaliseren van de VACUUMM-werking. Laten we wat terminologie begrijpen die later in deze blog zal worden gebruikt.
Zichtbaarheidskaart
Zoals de naam al aangeeft, houdt het zichtbaarheidsinformatie bij over pagina's die alleen tupels bevatten waarvan bekend is dat ze zichtbaar zijn voor alle actieve transacties. Voor elke pagina wordt één bit gebruikt. Als de bit is ingesteld op 1 betekent dat alle tupels van de bijbehorende pagina zichtbaar zijn. De bit ingesteld op 0 betekent dat er geen vrije ruimte is op de gegeven pagina en dat tupels zichtbaar kunnen zijn voor alle transacties.
Zichtbaarheidskaart wordt bijgehouden voor elke relatie (tabel en index) en wordt geassocieerd met hoofdrelaties, d.w.z. als de knooppuntnaam van het relatiebestand 12345 is, dan wordt het zichtbaarheidsbestand opgeslagen in het parallelle bestand 12345_vm.
Kaart vrije ruimte
Het houdt informatie over vrije ruimte bij met details over de beschikbare ruimte in de relatie. Dit wordt ook opgeslagen in het bestand parallel aan het hoofdbestand van de relatie, d.w.z. als de knooppuntnaam van het relatiebestand 12345 is, wordt het vrije ruimtekaartbestand opgeslagen in het parallelle bestand 12345_fsm.
Tuple bevriezen
PostgreSQL gebruikt 4 bytes voor het opslaan van transactie-ID's, wat betekent dat er maximaal 2 miljard transacties kunnen worden gegenereerd voordat het rond is. Overweeg nu nog steeds dat op dit moment een tuple een initiële transactie-ID bevat, zeg 100, en voor de nieuwe transactie (die de omwikkelde transactie gebruikt) zeg 5, zal transactie-ID 100 in de toekomst kijken en zal het de toegevoegde gegevens niet kunnen zien / gewijzigd door het, ook al was het eigenlijk in het verleden. Om deze speciale transactie-ID te vermijden, wordt FrozenTransactionId (gelijk aan 2) toegewezen. Deze speciale transactie-ID wordt altijd als in het verleden beschouwd en is zichtbaar voor alle transacties.
VACUM
De primaire taak van VACUUM is het terugwinnen van opslagruimte die wordt ingenomen door DODE tupels. Teruggewonnen opslagruimte wordt niet teruggegeven aan het besturingssysteem, maar wordt gewoon gedefragmenteerd binnen dezelfde pagina, dus ze zijn alleen beschikbaar om opnieuw te worden gebruikt door toekomstige gegevens in dezelfde tabel in te voegen. Terwijl de VACUUM-bewerking op een bepaalde tafel plaatsvindt, kunnen tegelijkertijd andere LEES-/SCHRIJF-bewerkingen op dezelfde tafel worden uitgevoerd, aangezien de exclusieve vergrendeling niet op de bepaalde tafel wordt uitgevoerd. Als er geen tabelnaam is opgegeven, wordt VACUUM uitgevoerd op alle tabellen van de database. De VACUUMM-bewerking voert een reeks bewerkingen uit binnen een ShareUpdateExclusive-slot:
- Scan alle pagina's van alle tabellen (of gespecificeerde tabel) van de database om alle dode tupels te krijgen.
- Bevries oude tuples indien nodig.
- Verwijder de index-tupel die naar de respectievelijke DEAD-tupels wijst.
- Verwijder de DEAD tuples van een pagina die overeenkomt met een specifieke tabel en wijs de live tupels opnieuw toe op de pagina.
- Update Free Space Map (FSM) en Visibility Map (VM).
- Knip indien mogelijk de laatste pagina in (als er DODE tupels waren die zijn vrijgekomen).
- Update alle bijbehorende systeemtabellen.
Zoals we kunnen zien aan de hand van de bovenstaande werkstappen voor VACUUM, is het duidelijk dat het een zeer kostbare operatie is omdat het alle pagina's van de relatie moet verwerken. Het is dus hard nodig om mogelijke pagina's over te slaan die niet gestofzuigd hoeven te worden. Aangezien Visibility map (VM) informatie geeft over de pagina waar, als er geen vrije ruimte is, kan worden aangenomen dat het bijbehorende paginavacuüm niet nodig is en daarom kan deze pagina veilig worden overgeslagen.
Aangezien VACUUM sowieso door alle pagina's en hun tupels gaat, maakt het van de gelegenheid gebruik om een andere belangrijke taak uit te voeren, namelijk het bevriezen van de kwalificerende tupels.
Volledig VACUM
Zoals besproken in de vorige sectie, hoewel VACUUM alle DEAD tupels verwijdert en de pagina defragmenteert voor toekomstig gebruik, helpt het niet bij het verminderen van de totale opslag van de tabel, aangezien er in feite geen ruimte vrijkomt voor de besturingssysteem. Stel dat een tabel tbl1 de totale opslag 1,5 GB heeft bereikt en van deze 1 GB bezet door dode tuple, dan zal na VACUUM nog eens ongeveer 1 GB beschikbaar zijn voor verdere tuple-invoeging, maar toch blijft de totale opslag 1,5 GB.
Volledig VACUUMM lost dit probleem op door ruimte vrij te maken en terug te geven aan het besturingssysteem. Maar dit heeft een prijs. In tegenstelling tot VACUM, staat VOL VACUM geen parallelle werking toe, omdat er een exclusieve vergrendeling nodig is voor de relatie die VOL VACUM wordt. Hieronder staan de stappen:
- Neemt exclusieve vergrendeling op de relatie.
- Maak een parallel leeg opslagbestand.
- Kopieer alle live tuples van de huidige opslag naar de nieuw toegewezen opslag.
- Maak dan de oorspronkelijke opslagruimte vrij.
- Maak het slot vrij.
Dus zoals blijkt uit de stappen, heeft het alleen opslagruimte nodig voor de resterende gegevens.
Automatisch VACUM
In plaats van VACUUM handmatig uit te voeren, ondersteunt PostgreSQL een demon die VACUUM periodiek automatisch activeert. Elke keer dat VACUUM wakker wordt (standaard 1 minuut), roept het meerdere werken op (afhankelijk van de configuratie van autovacuum_worker-processen).
Auto-vacuümwerkers voeren gelijktijdig VACUM-processen uit voor de respectievelijke aangewezen tabellen. Aangezien VACUUM geen exclusieve vergrendeling op tabellen accepteert, heeft het geen (of minimale) invloed op ander databasewerk.
De configuratie van Auto-VACUUM moet worden gedaan op basis van het gebruikspatroon van de database. Het mag niet te vaak voorkomen (omdat het de wekker van de werknemer verspilt omdat er mogelijk niet of te weinig dode tuples zijn) of te veel uitgesteld (het zal veel dode tuples samen veroorzaken en dus een opgeblazen gevoel).
VACUM of Volledig VACUM
Idealiter zou de databasetoepassing zo moeten worden ontworpen dat er geen VOLLEDIG VACUM nodig is. Zoals hierboven uitgelegd, creëert FULL VACUUM opslagruimte opnieuw en worden de gegevens teruggezet, dus als er alleen minder dode tupels zijn, wordt er onmiddellijk opslagruimte opnieuw gemaakt om alle originele gegevens terug te zetten. Omdat FULL VACUUM exclusief slot op de tafel neemt, blokkeert het alle bewerkingen op de overeenkomstige tafel. Dus FULL VACUUM doen kan soms de algehele database vertragen.
Samengevat moet volledig VACUUMM worden vermeden, tenzij bekend is dat het grootste deel van de opslagruimte wordt veroorzaakt door dode tupels. PostgreSQL-extensie pg_freespacemap kan worden gebruikt om een goede hint te krijgen over vrije ruimte.
Laten we een voorbeeld bekijken van het uitgelegde VACUUMM-proces.
Laten we eerst een tafeldemo1 maken:
postgres=# create table demo1(id int, id2 int);
CREATE TABLE
En voeg daar wat gegevens in:
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)
Laten we nu gegevens verwijderen:
postgres=# delete from demo1 where id%2=0;
DELETE 5000
En voer een handmatige stofzuiger uit:
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)
Deze vrije ruimte is nu beschikbaar voor hergebruik door PostgreSQL, maar als u die ruimte wilt vrijgeven aan het besturingssysteem, voert u het volgende uit:
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)
Conclusie
En dit was een kort voorbeeld van hoe het VACUUM-proces werkt. Gelukkig hoef je, dankzij het automatische vacuümproces, meestal en in een gewone PostgreSQL-omgeving, hier niet over na te denken, omdat het wordt beheerd door de motor zelf.