sql >> Databasteknik >  >> RDS >> PostgreSQL

När autovacuum inte dammsuger

För några veckor sedan förklarade jag grunderna för autovacuum tuning. I slutet av det inlägget lovade jag att snart undersöka problem med dammsugning. Tja, det tog lite längre tid än jag planerat, men nu kör vi.

För att snabbt sammanfatta, autovacuum är en bakgrundsprocess som städar upp döda rader, t.ex. gamla raderade radversioner. Du kan också utföra rensningen manuellt genom att köra VACUUM , men autovacuum gör det automatiskt beroende på mängden döda rader i tabellen, vid rätt tillfälle – inte för ofta men tillräckligt ofta för att hålla mängden "skräp" under kontroll.

Generellt sett, autovacuum kan inte köras för ofta – rensningen utförs endast efter att ha nått ett antal döda rader som samlas i tabellen. Men det kan bli försenat av olika anledningar, vilket resulterar i att tabeller och index blir större än önskvärt. Och det är precis ämnet för det här inlägget. Så vilka är de vanligaste bovarna och hur identifierar man dem?

Strypning

Som förklaras i grunderna för inställning, autovacuum arbetare stryps att endast utföra en viss mängd arbete per tidsintervall. Standardgränserna är ganska låga – cirka 4MB/s skrivning, 8MB/s läsning. Det är lämpligt för små maskiner som Raspberry Pi eller små servrar från 10 år sedan, men nuvarande maskiner är mycket kraftfullare (både när det gäller CPU och I/O) och hanterar mycket mer data.

Föreställ dig att du har några stora bord och några små. Om alla tre autovacuum arbetare börjar städa upp de stora borden, inget av de små borden kommer att dammsugas oavsett mängden döda rader de samlar på sig. Att identifiera detta är inte särskilt svårt, förutsatt att du har tillräcklig övervakning. Leta efter perioder när alla autovacuum arbetare är upptagna medan bord inte dammsugs trots att de samlat på sig många döda rader.

All nödvändig information finns i pg_stat_activity (antal autovacuum arbetsprocesser) och pg_stat_all_tables (last_autovacuum och n_dead_tup ).

Ökar antalet autovacuum arbetare är ingen lösning, eftersom den totala mängden arbete förblir densamma. Du kan ange begränsningar för strypning per bord, exkludera den arbetaren från den totala gränsen, men det garanterar fortfarande inte att det kommer att finnas tillgängliga arbetare när det behövs.

Den rätta lösningen är att justera strypningen med gränser som är rimliga med avseende på hårdvarukonfigurationen och arbetsbelastningsmönster. Några grundläggande strypningsrekommendationer nämns i föregående inlägg. (Självklart, om du kan minska mängden döda rader som genereras i databasen, skulle det vara en idealisk lösning.)

Från denna punkt antar vi att strypningen inte är problemet, det vill säga att autovacuum arbetare inte är mättade under långa perioder och att rensningen utlöses på alla bord utan orimliga förseningar.

Långa transaktioner

Så om bordet dammsugs regelbundet, kan det väl inte samlas på många döda rader, eller hur? Tyvärr inte. Raderna är faktiskt inte "borttagbara" direkt efter att de tagits bort, utan bara när det inte finns några transaktioner som kan se dem. Det exakta beteendet beror på vad de andra transaktionerna gör och serialiseringsnivån, men generellt:

LÄS ENGAGERAG

  • körning av frågor blockerar rengöring
  • Inaktiva transaktioner blockerar endast rensning om de utförde en skrivning
  • Inaktiva transaktioner (utan några skrivningar) blockerar inte rensning (men det är inte en bra praxis att hålla dem kvar ändå)

SERIALISERBAR

  • körning av frågor blockerar rengöring
  • Inaktiva transaktioner blockerar rensning (även om de bara läste)

I praktiken är det naturligtvis mer nyanserat, men att förklara alla de olika bitarna skulle kräva att man först förklarade hur XID och ögonblicksbilder fungerar, och det är inte målet med det här inlägget. Vad du egentligen borde ta bort från detta är att långa transaktioner är en dålig idé, särskilt om dessa transaktioner kan ha gjort skriverier.

Naturligtvis finns det fullt giltiga skäl till varför du kan behöva behålla transaktioner under långa perioder (t.ex. om du behöver säkerställa ACID för alla ändringar). Men se till att det inte händer i onödan, t.ex. på grund av en dålig applikationsdesign.

En något oväntad konsekvens av detta är hög CPU- och I/O-användning, på grund av autovacuum köra om och om igen, utan att rengöra några döda rader (eller bara några av dem). På grund av det är borden fortfarande kvalificerade för städning i nästa omgång, vilket orsakar mer skada än nytta.

Hur upptäcker man detta? För det första måste du övervaka långvariga transaktioner, särskilt inaktiva. Allt du behöver göra är att läsa data från pg_stat_activity . Vydefinitionen ändras lite med PostgreSQL-versionen, så du kan behöva justera detta lite:

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 också helt enkelt använda några befintliga övervakningsplugin, t.ex. check_postgres.pl. De inkluderar redan den här typen av hälsokontroller. Du måste bestämma vad som är en rimlig transaktions-/förfrågans varaktighet, som är applikationsspecifik.

Sedan PostgreSQL 9.6 kan du även använda idle_in_transaction_session_timeout så att transaktioner som har varit lediga för länge avslutas automatiskt. På samma sätt finns det statement_timeout för långa frågor .

En annan användbar sak är VACUUM VERBOSE som faktiskt kommer att berätta hur många döda rader som inte kunde tas bort ännu:

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 kommer inte att berätta vilken backend som förhindrar rensningen, men det är ett ganska tydligt tecken på vad som händer.

Obs! . Du kan inte enkelt få den här informationen från autovacuum eftersom det bara loggas med DEBUG2 som standard (och du vill absolut inte köra med den loggnivån i produktionen).

Långa frågor i heta standbylägen

Låt oss anta att tabeller dammsugs i tid, men inte tar bort döda tuplar, vilket resulterar i tabell- och indexsvällning. Du övervakar pg_stat_activity och det finns inga långa transaktioner. Vad kan problemet vara?

Om du har en strömmande replika är chansen stor att problemet kan vara där. Om repliken använder hot_standby_feedback=on , frågor på repliken fungerar i stort sett som transaktioner på den primära, inklusive blockering av rensning. Naturligtvis hot_standby_feedback=on används exakt när du kör långa frågor (t.ex. analys och BI-arbetsbelastningar) på repliker, för att förhindra avbokningar på grund av replikeringskonflikter.

Tyvärr måste du välja – antingen behåll hot_standby_feedback=on och acceptera förseningar i städning, eller hantera inställda frågor. Du kan också använda max_standby_streaming_delay för att begränsa effekten, även om det inte hindrar avbokningarna helt (så du måste fortfarande försöka igen).

Det finns faktiskt ett tredje alternativ nu - logisk replikering. Istället för att använda fysisk strömmande replikering för BI-repliken kan du kopiera ändringarna med den nya logiska replikeringen, som finns tillgänglig i PostgreSQL 10. Logisk replikering mjukar upp kopplingen mellan primär och replik, och gör klustren mestadels oberoende (rensas upp oberoende, etc.).

Detta löser de två problem som är förknippade med fysisk streamingreplikering – fördröjd rensning av primära eller avbrutna frågor på BI-repliken. För repliker som tjänar DR-ändamål återstår dock streaming-replikering att vara det rätta valet. Men dessa repliker kör inte (eller borde inte vara) långa frågor.

Obs! Medan jag nämnde att logisk replikering kommer att vara tillgänglig i PostgreSQL 10, var en betydande del av infrastrukturen tillgänglig i tidigare utgåvor (särskilt PostgreSQL 9.6). Så du kanske kan göra detta även på äldre versioner (vi gjorde det för några av våra kunder), men PostgreSQL 10 kommer att göra det mycket bekvämare och bekvämare.

Problem med autoanalyze

En detalj du kanske missar är det autovacuum arbetare utför faktiskt två olika uppgifter. Först rensningen (som om du kör VACUUM ), men också samla in statistik (som om du kör ANALYZE ). Och båda delar stryps med autovacuum_cost_limit .

Men det är stor skillnad i att hantera transaktioner. När VACUUM del når autovacuum_cost_limit , släpper arbetaren ögonblicksbilden och sover en stund. ANALYZE måste dock köras i en enda ögonblicksbild/transaktion, vilket gör blockera rengöring.

Det här är ett elegant sätt att skjuta dig själv i foten, särskilt om du också gör något av det här:

  • öka default_statistics_target att bygga mer exakt statistik från större urval
  • lägre autovacuum_analyze_scale_factor för att samla in statistik oftare

Den oavsiktliga konsekvensen är förstås att ANALYZE kommer att hända oftare, kommer att ta mycket längre tid och kommer (till skillnad från VACUUM). del) förhindra sanering. Lösningen är vanligtvis ganska enkel – sänk inte autovacuum_analyze_scale_factor för mycket. Kör ANALYZE varje gång 10 % av tabelländringarna borde vara mer än tillräckligt i de flesta fall.

n_dead_tup

En sista sak jag skulle vilja nämna är ändringar i pg_stat_all_tables.n_dead_tup värden. Du kanske tror att värdet är en enkel räknare, som ökas när en ny död tuppel skapas och minskas när den rensas upp. Men det är faktiskt bara en uppskattning av antalet döda tuplar, uppdaterad av ANALYZE . För små tabeller (mindre än 240 MB) är det egentligen ingen stor skillnad, eftersom ANALYZE läser hela tabellen och så är den ganska exakt. För stora tabeller kan det dock ändras en hel del beroende på vilken delmängd av tabellen som samplas. Och sänker autovacuum_vacuum_scale_factor gör det mer slumpmässigt.

Så var försiktig när du tittar på n_dead_tup i ett övervakningssystem. Plötsliga fall eller ökningar av värdet kan helt enkelt bero på ANALYZE beräknar om en annan uppskattning och inte på grund av faktisk rensning och/eller nya döda tuplar som dyker upp i tabellen.

Sammanfattning

För att sammanfatta detta i några enkla punkter:

  • autovacuum kan bara göra det om det inte finns några transaktioner som kan behöva de döda tuplarna.
  • Långa frågor blockerar rensning. Överväg att använda statement_timeout för att begränsa skadan.
  • Långvarig transaktion kan blockera rensning. Det exakta beteendet beror på saker som isoleringsnivå eller vad som hände i transaktionen. Övervaka dem och avsluta dem om möjligt.
  • Långvariga frågor om repliker med hot_standby_feedback=on kan också blockera rensning.
  • autoanalyze är också strypt, men till skillnad från VACUUM del den behåller en enda ögonblicksbild (och blockerar därmed rensning).
  • n_dead_tup är bara en uppskattning som underhålls av ANALYZE , så förvänta dig vissa fluktuationer (särskilt på stora bord).

  1. Sparar ändringar efter tabellredigering i SQL Server Management Studio

  2. Fixa "Konvertering misslyckades vid konvertering av varchar-värdet" När du försöker sammanfoga i SQL Server

  3. Hur man fyller i saknade datum i PostgreSQL med generation_series

  4. Hur får man en ålder från ett D.O.B-fält i MySQL?