sql >> Database >  >> RDS >> PostgreSQL

Wanneer autovacuüm niet stofzuigt

Een paar weken geleden heb ik de basisprincipes van autovacuümtuning uitgelegd. Aan het einde van die post beloofde ik binnenkort problemen met stofzuigen te onderzoeken. Nou, het heeft wat langer geduurd dan ik had gepland, maar daar gaan we dan.

Om snel samen te vatten, autovacuum is een achtergrondproces voor het opruimen van dode rijen, b.v. oude verwijderde rijversies. U kunt het opschonen ook handmatig uitvoeren door VACUUM . uit te voeren , maar autovacuum doet dat automatisch, afhankelijk van het aantal dode rijen in de tabel, op het juiste moment – ​​niet te vaak maar vaak genoeg om de hoeveelheid “vuilnis” onder controle te houden.

Over het algemeen geldt:autovacuum kan niet te vaak worden uitgevoerd - het opschonen wordt alleen uitgevoerd nadat een aantal dode rijen in de tabel is bereikt. Maar het kan om verschillende redenen vertraging oplopen, waardoor tabellen en indexen groter worden dan wenselijk. En dat is precies het onderwerp van dit bericht. Dus wat zijn de veelvoorkomende boosdoeners en hoe kunnen ze worden geïdentificeerd?

Beperking

Zoals uitgelegd in de basis van afstemmen, autovacuum werknemers worden gesmoord om slechts een bepaalde hoeveelheid werk per tijdsinterval uit te voeren. De standaardlimieten zijn vrij laag - ongeveer 4 MB/s aan schrijfbewerkingen, 8 MB/s aan leesbewerkingen. Dat is geschikt voor kleine machines zoals Raspberry Pi of kleine servers van 10 jaar geleden, maar de huidige machines zijn veel krachtiger (zowel qua CPU als I/O) en verwerken veel meer data.

Stel je voor dat je een paar grote tafels hebt en een paar kleine. Als alle drie autovacuum werknemers beginnen met het opruimen van de grote tafels, geen van de kleine tafels wordt gestofzuigd, ongeacht het aantal dode rijen dat ze ophopen. Het identificeren hiervan is niet bijzonder moeilijk, ervan uitgaande dat u voldoende toezicht hebt. Zoek naar perioden waarin alle autovacuum werknemers zijn druk terwijl tafels niet worden gestofzuigd, ondanks het feit dat er veel lege rijen zijn.

Alle benodigde informatie staat in pg_stat_activity (aantal autovacuum werkprocessen) en pg_stat_all_tables (last_autovacuum en n_dead_tup ).

Verhogen van het aantal autovacuum werknemers is geen oplossing, aangezien de totale hoeveelheid werk gelijk blijft. U kunt beperkingslimieten per tabel specificeren, waarbij die werknemer wordt uitgesloten van de totale limiet, maar dat garandeert nog steeds niet dat er beschikbare werknemers zullen zijn wanneer dat nodig is.

De juiste oplossing is het afstellen van de beperking, waarbij redelijke limieten worden gehanteerd met betrekking tot de hardwareconfiguratie en werkbelastingpatronen. Enkele basisaanbevelingen voor beperking worden genoemd in het vorige bericht. (Als u het aantal dode rijen dat in de database wordt gegenereerd kunt verminderen, zou dat natuurlijk een ideale oplossing zijn.)

Vanaf dit punt gaan we ervan uit dat de beperking niet het probleem is, d.w.z. dat de autovacuum werknemers niet voor lange tijd verzadigd zijn en dat het opruimen zonder onredelijke vertragingen op alle tafels wordt gestart.

Lange transacties

Dus als de tafel regelmatig wordt gestofzuigd, kan hij toch niet veel dode rijen verzamelen, toch? Helaas niet. De rijen zijn niet direct "verwijderbaar" nadat ze zijn verwijderd, maar alleen als er geen transacties zijn die ze mogelijk kunnen zien. Het exacte gedrag hangt af van wat de andere transacties doen (waren) en het serialisatieniveau, maar in het algemeen:

LEES TOEGEVOEGD

  • lopende query's blokkeren opschonen
  • niet-actieve transacties blokkeren alleen opschonen als ze een schrijfactie hebben uitgevoerd
  • niet-actieve transacties (zonder schrijfacties) blokkeren het opschonen niet (maar het is toch geen goede gewoonte om ze in de buurt te houden)

SERIALISEERBAAR

  • lopende query's blokkeren opschonen
  • niet-actieve transacties blokkeren opschonen (zelfs als ze alleen lezen)

In de praktijk ligt het natuurlijk genuanceerder, maar om alle verschillende bits uit te leggen, moet eerst worden uitgelegd hoe XID's en snapshots werken, en dat is niet het doel van dit bericht. Wat je hier echt uit moet halen, is dat lange transacties een slecht idee zijn, vooral als die transacties schrijfacties hebben veroorzaakt.

Natuurlijk zijn er volkomen geldige redenen waarom u transacties voor langere tijd moet bewaren (bijvoorbeeld als u voor alle wijzigingen ACID moet garanderen). Maar zorg ervoor dat het niet onnodig gebeurt, b.v. vanwege een slecht applicatieontwerp.

Een enigszins onverwacht gevolg hiervan is een hoog CPU- en I/O-gebruik, vanwege autovacuum keer op keer rennen, zonder enige dode rijen (of slechts een paar ervan) op te ruimen. Hierdoor komen de tafels nog steeds in aanmerking voor opruiming in de volgende ronde, wat meer kwaad dan goed doet.

Hoe dit te detecteren? Ten eerste moet u langlopende transacties volgen, met name inactieve transacties. Het enige dat u hoeft te doen, is gegevens lezen van pg_stat_activity . De weergavedefinitie verandert een beetje met de PostgreSQL-versie, dus het kan zijn dat je dit een beetje moet aanpassen:

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'

U kunt ook gewoon een bestaande monitoring-plug-in gebruiken, b.v. check_postgres.pl. Die bevatten al dit soort geestelijke gezondheidscontroles. U moet beslissen wat een redelijke transactie-/queryduur is, die toepassingsspecifiek is.

Sinds PostgreSQL 9.6 kunt u ook idle_in_transaction_session_timeout gebruiken zodat transacties die te lang inactief zijn, automatisch worden beëindigd. Evenzo is er voor lange zoekopdrachten statement_timeout .

Een ander handig ding is VACUUM VERBOSE die u daadwerkelijk zal vertellen hoeveel dode rijen nog niet konden worden verwijderd:

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.
...

Het zal u niet vertellen welke backend het opschonen verhindert, maar het is een vrij duidelijk teken van wat er gebeurt.

Opmerking: . U kunt deze informatie niet gemakkelijk uit autovacuum halen omdat het alleen is gelogd met DEBUG2 standaard (en je wilt zeker niet werken met dat logniveau in productie).

Lange zoekopdrachten op actieve standbys

Laten we aannemen dat tabellen tijdig worden gestofzuigd, maar dat dode tupels niet worden verwijderd, wat resulteert in een opgeblazen tafel en index. Je volgt pg_stat_activity en er zijn geen langlopende transacties. Wat kan het probleem zijn?

Als je een streaming-replica hebt, is de kans groot dat het probleem daar is. Als de replica hot_standby_feedback=on . gebruikt , werken query's op de replica min of meer als transacties op de primaire, inclusief het blokkeren van opschonen. Natuurlijk, hot_standby_feedback=on wordt precies gebruikt bij het uitvoeren van lange query's (bijvoorbeeld analyses en BI-workloads) op replica's, om annuleringen als gevolg van replicatieconflicten te voorkomen.

Helaas moet je kiezen – ofwel hot_standby_feedback=on . houden en accepteer vertragingen bij het opruimen, of behandel geannuleerde vragen. U kunt ook max_standby_streaming_delay . gebruiken om de impact te beperken, hoewel dat de annuleringen niet volledig verhindert (je moet de zoekopdrachten dus nog steeds opnieuw proberen).

Eigenlijk is er nu een derde optie:logische replicatie. In plaats van fysieke streaming-replicatie voor de BI-replica te gebruiken, kunt u de wijzigingen kopiëren met behulp van de nieuwe logische replicatie, beschikbaar in PostgreSQL 10. Logische replicatie versoepelt de koppeling tussen de primaire en replica en maakt de clusters grotendeels onafhankelijk (worden onafhankelijk opgeschoond, enz.).

Dit lost de twee problemen op die verband houden met fysieke streamingreplicatie:vertraagde opschoning van primaire of geannuleerde query's op de BI-replica. Voor replica's die DR-doeleinden dienen, blijft streaming-replicatie echter de juiste keuze. Maar die replica's voeren geen lange zoekopdrachten uit (of zouden dat niet moeten zijn).

Opmerking: Hoewel ik al zei dat logische replicatie beschikbaar zal zijn in PostgreSQL 10, was een aanzienlijk deel van de infrastructuur beschikbaar in eerdere releases (met name PostgreSQL 9.6). Dus je kunt dit misschien zelfs doen met oudere releases (we hebben dat voor sommige van onze klanten gedaan), maar PostgreSQL 10 zal het veel handiger en comfortabeler maken.

Probleem met autoanalyze

Een detail dat u misschien mist, is dat autovacuum werknemers voeren eigenlijk twee verschillende taken uit. Ten eerste de opschoning (alsof u VACUUM uitvoert ), maar ook het verzamelen van statistieken (alsof u ANALYZE uitvoert) ). En beide onderdelen worden gesmoord met behulp van autovacuum_cost_limit .

Maar er is een groot verschil in het afhandelen van transacties. Telkens wanneer de VACUUM deel bereikt autovacuum_cost_limit , geeft de werknemer de momentopname vrij en slaapt een tijdje. De ANALYZE moet echter in een enkele momentopname/transactie worden uitgevoerd, wat doet opruimen blokkeren.

Dit is een elegante manier om jezelf in de voet te schieten, vooral als je ook iets van dit doet:

  • verhoog default_statistics_target om nauwkeurigere statistieken op te bouwen uit grotere steekproeven
  • lager autovacuum_analyze_scale_factor om vaker statistieken te verzamelen

Het onbedoelde gevolg is natuurlijk dat ANALYZE zal vaker gebeuren, zal veel langer duren en zal (in tegenstelling tot de VACUUM deel) opruimen te voorkomen. De oplossing is meestal vrij eenvoudig - verlaag de autovacuum_analyze_scale_factor niet te veel. ANALYZE uitvoeren elke keer zou 10% van de tabelwijzigingen in de meeste gevallen meer dan genoeg moeten zijn.

n_dead_tup

Een laatste ding dat ik zou willen noemen is over veranderingen in pg_stat_all_tables.n_dead_tup waarden. Je zou kunnen denken dat de waarde een eenvoudige teller is, die wordt verhoogd wanneer een nieuwe dode tuple wordt gemaakt en wordt verlaagd wanneer deze wordt opgeruimd. Maar het is eigenlijk slechts een schatting van het aantal dode tuples, bijgewerkt door ANALYZE . Voor kleine tabellen (minder dan 240 MB) is het niet echt een groot verschil, omdat ANALYZE leest de hele tabel en is dus vrij exact. Voor grote tabellen kan het echter nogal veranderen, afhankelijk van welke subset van tabellen wordt bemonsterd. En het verlagen van autovacuum_vacuum_scale_factor maakt het meer willekeurig.

Wees dus voorzichtig bij het bekijken van n_dead_tup in een monitoringsysteem. Plotselinge dalingen of stijgingen van de waarde kunnen eenvoudig te wijten zijn aan ANALYZE het opnieuw berekenen van een andere schatting, en niet vanwege daadwerkelijke opschoning en/of nieuwe dode tupels die in de tabel verschijnen.

Samenvatting

Om dit samen te vatten in een paar simpele punten:

  • autovacuum kan zijn werk alleen doen als er geen transacties zijn die de dode tuples nodig hebben.
  • Langlopende zoekopdrachten blokkeren opschonen. Overweeg het gebruik van statement_timeout om de schade te beperken.
  • Langlopende transactie kan opschonen blokkeren. Het exacte gedrag hangt af van zaken als isolatieniveau of wat er in de transactie is gebeurd. Controleer ze en beëindig ze indien mogelijk.
  • Langlopende zoekopdrachten op replica's met hot_standby_feedback=on kan ook het opruimen blokkeren.
  • autoanalyze wordt ook gesmoord, maar in tegenstelling tot de VACUUM deel houdt het een enkele momentopname bij (en blokkeert dus opschonen).
  • n_dead_tup is slechts een schatting die wordt onderhouden door ANALYZE , dus verwacht wat schommelingen (vooral op grote tafels).

  1. Hoe een MySQL-query plannen?

  2. SWITCHOFFSET() Voorbeelden in SQL Server

  3. Hoe lees ik de inhoud van een .sql-bestand in een R-script om een ​​query uit te voeren?

  4. Hoe kan ik een MySQL-query versnellen met een grote offset in de LIMIT-clausule?