sql >> Database teknologi >  >> RDS >> PostgreSQL

Når autovakuum ikke støvsuger

For et par uger siden forklarede jeg det grundlæggende i autovakuum tuning. I slutningen af ​​det indlæg lovede jeg snart at undersøge problemer med støvsugning. Nå, det tog lidt længere tid, end jeg havde planlagt, men nu går vi.

For hurtigt at opsummere, autovacuum er en baggrundsproces, der rydder op i døde rækker, f.eks. gamle slettede rækkeversioner. Du kan også udføre oprydningen manuelt ved at køre VACUUM , men autovacuum gør det automatisk afhængigt af mængden af ​​døde rækker i tabellen på det rigtige tidspunkt - ikke for ofte, men ofte nok til at holde mængden af ​​"skrald" under kontrol.

Generelt set autovacuum kan ikke køre for ofte - oprydningen udføres kun efter at have nået et antal døde rækker, der akkumuleres i tabellen. Men det kan blive forsinket af forskellige årsager, hvilket resulterer i, at tabeller og indekser bliver større end ønskeligt. Og det er netop emnet for dette indlæg. Så hvad er de almindelige syndere, og hvordan identificerer man dem?

Throttling

Som forklaret i grundlæggende tuning, autovacuum arbejdere begrænses til kun at udføre en vis mængde arbejde pr. tidsinterval. Standardgrænserne er ret lave – omkring 4 MB/s skrivning, 8 MB/s læsninger. Det er velegnet til små maskiner som Raspberry Pi eller små servere for 10 år siden, men de nuværende maskiner er langt mere kraftfulde (både med hensyn til CPU og I/O) og håndterer meget flere data.

Forestil dig, at du har et par store borde og nogle små. Hvis alle tre autovacuum arbejdere begynder at rydde op i de store borde, ingen af ​​de små borde bliver støvsuget uanset mængden af ​​døde rækker, de akkumulerer. Det er ikke særlig svært at identificere dette, forudsat at du har tilstrækkelig overvågning. Se efter perioder, hvor alle autovacuum arbejdere har travlt, mens borde ikke støvsuges på trods af, at de har akkumuleret mange døde rækker.

Alle de nødvendige oplysninger er i pg_stat_activity (antal autovacuum arbejdsprocesser) og pg_stat_all_tables (last_autovacuum og n_dead_tup ).

Forøgelse af antallet af autovacuum arbejdere er ikke en løsning, da den samlede mængde arbejde forbliver den samme. Du kan angive spjældgrænser pr. bord, udelukke den pågældende arbejder fra den samlede grænse, men det garanterer stadig ikke, at der vil være ledige arbejdere, når det er nødvendigt.

Den rigtige løsning er at justere reguleringen ved at bruge grænser, der er rimelige med hensyn til hardwarekonfiguration og arbejdsbelastningsmønstre. Nogle grundlæggende reguleringsanbefalinger er nævnt i det forrige indlæg. (Hvis du kan reducere mængden af ​​døde rækker, der genereres i databasen, ville det naturligvis være en ideel løsning.)

Fra dette tidspunkt vil vi antage, at reguleringen ikke er problemet, dvs. at autovacuum arbejdere ikke er mættede i lange perioder, og at oprydningen udløses på alle borde uden urimelige forsinkelser.

Lange transaktioner

Så hvis bordet støvsuges regelmæssigt, kan det vel ikke akkumulere mange døde rækker, vel? Desværre ikke. Rækkerne er faktisk ikke "fjernbare" umiddelbart efter at de er blevet slettet, men kun når der ikke er nogen transaktioner, der muligvis kan se dem. Den nøjagtige adfærd afhænger af, hvad de andre transaktioner (var) i gang med og serialiseringsniveau, men generelt:

LÆS ENGAGEMENT

  • kørende forespørgsler blokerer oprydning
  • inaktive transaktioner blokerer kun for oprydning, hvis de udførte en skrivning
  • tomgangstransaktioner (uden skrivninger) blokerer ikke for oprydning (men det er alligevel ikke en god praksis at beholde dem)

SERIALISERBAR

  • kørende forespørgsler blokerer oprydning
  • inaktive transaktioner blokerer for oprydning (selvom de kun læste)

I praksis er det selvfølgelig mere nuanceret, men at forklare alle de forskellige bits ville kræve først at forklare, hvordan XID'er og snapshots fungerer, og det er ikke målet med dette indlæg. Hvad du virkelig bør tage væk fra dette er, at lange transaktioner er en dårlig idé, især hvis disse transaktioner måske har skrevet.

Selvfølgelig er der helt gyldige grunde til, at du muligvis skal beholde transaktioner i lange perioder (f.eks. hvis du skal sikre ACID for alle ændringerne). Men sørg for, at det ikke sker unødigt, f.eks. på grund af et dårligt applikationsdesign.

En noget uventet konsekvens af dette er højt CPU- og I/O-forbrug på grund af autovacuum kører igen og igen, uden at rense nogen døde rækker (eller blot nogle få af dem). Derfor er bordene stadig kvalificerede til oprydning i næste runde, hvilket forårsager mere skade end gavn.

Hvordan opdager man dette? For det første skal du overvåge langvarige transaktioner, især inaktive. Alt du skal gøre er at læse data fra pg_stat_activity . Visningsdefinitionen ændrer sig lidt med PostgreSQL-versionen, så du skal muligvis justere denne lidt:

SELECT xact_start, state FROM pg_stat_activity;

-- count 'idle' transactions longer than 15 minutes (since BEGIN)
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - xact_start) > interval '15 minutes'

-- count transactions 'idle' for more than 5 minutes
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - state_change) > interval '5 minutes'

Du kan også blot bruge nogle eksisterende overvågningsplugin, f.eks. check_postgres.pl. Disse inkluderer allerede denne type sundhedstjek. Du skal beslutte, hvad der er en rimelig transaktion/forespørgselsvarighed, som er applikationsspecifik.

Siden PostgreSQL 9.6 kan du også bruge idle_in_transaction_session_timeout så transaktioner, der har været inaktive for længe, ​​afsluttes automatisk. Tilsvarende er der for lange forespørgsler statement_timeout .

En anden nyttig ting er VACUUM VERBOSE som faktisk vil fortælle dig, hvor mange døde rækker der ikke kunne fjernes endnu:

db=# VACUUM verbose z;
INFO:  vacuuming "public.z"
INFO:  "z": found 0 removable, 66797 nonremovable row versions in 443 out of 443 pages
DETAIL:  12308 dead row versions cannot be removed yet.
...

Det vil ikke fortælle dig, hvilken backend der forhindrer oprydningen, men det er et ret tydeligt tegn på, hvad der sker.

Bemærk: . Du kan ikke nemt få disse oplysninger fra autovacuum fordi det kun er logget med DEBUG2 som standard (og du vil bestemt ikke køre med det logniveau i produktionen).

Lange forespørgsler på varme standbys

Lad os antage, at borde bliver støvsuget rettidigt, men ikke fjerner døde tupler, hvilket resulterer i bord- og indeksbloat. Du overvåger pg_stat_activity og der er ingen langvarige transaktioner. Hvad kan problemet være?

Hvis du har en streaming-replika, er der sandsynlighed for, at problemet kan være der. Hvis replikaen bruger hot_standby_feedback=on , forespørgsler på replikaen fungerer stort set som transaktioner på den primære, inklusive blokering af oprydning. Selvfølgelig, hot_standby_feedback=on bruges nøjagtigt, når der køres lange forespørgsler (f.eks. analyser og BI-arbejdsbelastninger) på replikaer, for at forhindre annulleringer på grund af replikeringskonflikter.

Desværre bliver du nødt til at vælge – enten behold hot_standby_feedback=on og acceptere forsinkelser i oprydningen, eller håndtere annullerede forespørgsler. Du kan også bruge max_standby_streaming_delay for at begrænse virkningen, selvom det ikke forhindrer aflysningerne helt (så du skal stadig prøve forespørgslerne igen).

Faktisk er der en tredje mulighed nu - logisk replikering. I stedet for at bruge fysisk streaming replikering til BI replikaen, kan du kopiere ændringerne ved hjælp af den nye logiske replikering, tilgængelig i PostgreSQL 10. Logisk replikering afslapper koblingen mellem den primære og replikaen, og gør klyngerne for det meste uafhængige (ryddes op uafhængigt, osv.).

Dette løser de to problemer forbundet med fysisk streaming-replikering – forsinket oprydning på primære eller annullerede forespørgsler på BI-replikaen. Til replikaer, der tjener DR-formål, er streaming-replikering dog stadig det rigtige valg. Men disse replikaer kører ikke (eller burde ikke køre) lange forespørgsler.

Bemærk: Mens jeg nævnte, at logisk replikering vil være tilgængelig i PostgreSQL 10, var en betydelig del af infrastrukturen tilgængelig i tidligere udgivelser (især PostgreSQL 9.6). Så du kan muligvis gøre dette selv på ældre udgivelser (det gjorde vi for nogle af vores kunder), men PostgreSQL 10 vil gøre det meget mere praktisk og behageligt.

Problemer med autoanalyze

En detalje, du måske går glip af, er autovacuum arbejdere udfører faktisk to forskellige opgaver. Først oprydningen (som om du kører VACUUM ), men også indsamling af statistik (som om du kører ANALYZE ). Og begge dele dele er droslet ved hjælp af autovacuum_cost_limit .

Men der er stor forskel på at håndtere transaktioner. Hver gang VACUUM del når autovacuum_cost_limit , frigiver arbejderen øjebliksbilledet og sover et stykke tid. ANALYZE skal dog køre i et enkelt øjebliksbillede/transaktion, hvilket gør blokere oprydning.

Dette er en elegant måde at skyde dig selv i foden på, især hvis du også gør noget af dette:

  • øg default_statistics_target at opbygge mere nøjagtig statistik fra større stikprøver
  • lavere autovacuum_analyze_scale_factor at indsamle statistik oftere

Den utilsigtede konsekvens er selvfølgelig, at ANALYZE vil ske hyppigere, vil tage meget længere tid og vil (i modsætning til VACUUM). del) forhindre oprydning. Løsningen er normalt ret enkel – sænk ikke autovacuum_analyze_scale_factor for meget. Kører ANALYZE hver gang 10 % af tabelændringerne burde være mere end nok i de fleste tilfælde.

n_dead_tup

En sidste ting, jeg gerne vil nævne, er om ændringer i pg_stat_all_tables.n_dead_tup værdier. Du tror måske, at værdien er en simpel tæller, der øges, når en ny død tupel oprettes, og nedsættes, når den er ryddet op. Men det er faktisk kun et skøn over antallet af døde tupler, opdateret af ANALYZE . For små borde (mindre end 240 MB) er det egentlig ikke den store forskel, fordi ANALYZE læser hele tabellen og så er den ret præcis. For store tabeller kan det dog ændre sig en del afhængigt af hvilken undergruppe af tabellen, der bliver samplet. Og sænker autovacuum_vacuum_scale_factor gør det mere tilfældigt.

Så vær forsigtig, når du ser på n_dead_tup i et overvågningssystem. Pludselige fald eller stigninger i værdien kan simpelthen skyldes ANALYZE genberegning af et andet estimat og ikke på grund af faktisk oprydning og/eller nye døde tupler, der dukker op i tabellen.

Oversigt

For at opsummere dette i et par enkle punkter:

  • autovacuum kan kun gøre dets arbejde, hvis der ikke er nogen transaktioner, der måske har brug for de døde tupler.
  • Langevarende forespørgsler blokerer for oprydning. Overvej at bruge statement_timeout for at begrænse skaden.
  • Langevarende transaktion kan blokere for oprydning. Den nøjagtige adfærd afhænger af ting som isolationsniveau eller hvad der skete i transaktionen. Overvåg dem og afslut dem, hvis det er muligt.
  • Langevarende forespørgsler på replikaer med hot_standby_feedback=on kan også blokere for oprydning.
  • autoanalyze er også droslet, men i modsætning til VACUUM del det holder et enkelt øjebliksbillede (og dermed blokerer oprydning).
  • n_dead_tup er kun et estimat vedligeholdt af ANALYZE , så forvent nogle udsving (især på store borde).

  1. SQL tæller alle rækker i stedet for at tælle individuelle rækker

  2. Tjek, om der allerede findes en brugerdefineret type i PostgreSQL

  3. Kan du oprette et indeks i CREATE TABLE definitionen?

  4. Kan jeg tilføje en UNIK begrænsning til en PostgreSQL-tabel, efter at den allerede er oprettet?