sql >> Databasteknik >  >> RDS >> PostgreSQL

Ett prestationsfuskblad för PostgreSQL

Prestanda är en av de viktigaste och mest komplexa uppgifterna när man hanterar en databas. Det kan påverkas av konfigurationen, hårdvaran eller till och med systemets design. Som standard är PostgreSQL konfigurerad med kompatibilitet och stabilitet i åtanke, eftersom prestandan beror mycket på hårdvaran och på vårt system i sig. Vi kan ha ett system med mycket data som läses men informationen ändras inte ofta. Eller så kan vi ha ett system som skriver kontinuerligt. Av denna anledning är det omöjligt att definiera en standardkonfiguration som fungerar för alla typer av arbetsbelastningar.

I den här bloggen kommer vi att se hur man går tillväga för att analysera arbetsbelastningen, eller frågorna, som körs. Vi ska sedan granska några grundläggande konfigurationsparametrar för att förbättra prestandan för vår PostgreSQL-databas. Som vi nämnde kommer vi bara att se några av parametrarna. Listan över PostgreSQL-parametrar är omfattande, vi skulle bara beröra några av de viktigaste. Men man kan alltid konsultera den officiella dokumentationen för att fördjupa sig i de parametrar och konfigurationer som verkar viktigast eller användbara i vår miljö.

FÖRKLARA

Ett av de första stegen vi kan ta för att förstå hur vi kan förbättra vår databas prestanda är att analysera de frågor som görs.

PostgreSQL utformar en frågeplan för varje fråga den tar emot. För att se den här planen använder vi EXPLAIN.

Strukturen för en frågeplan är ett träd av plannoder. Noderna i den nedre nivån av trädet är skanningsnoder. De returnerar råa rader från en tabell. Det finns olika typer av skanningsnoder för olika metoder för åtkomst till tabellen. EXPLAIN-utgången har en rad för varje nod i planträdet.

world=# EXPLAIN SELECT * FROM city t1,country t2 WHERE id>100 AND t1.population>700000 AND t2.population<7000000;
                               QUERY PLAN                                
--------------------------------------------------------------------------
Nested Loop  (cost=0.00..734.81 rows=50662 width=144)
  ->  Seq Scan on city t1  (cost=0.00..93.19 rows=347 width=31)
        Filter: ((id > 100) AND (population > 700000))
  ->  Materialize  (cost=0.00..8.72 rows=146 width=113)
        ->  Seq Scan on country t2  (cost=0.00..7.99 rows=146 width=113)
              Filter: (population < 7000000)
(6 rows)

Det här kommandot visar hur tabellerna i vår fråga kommer att skannas. Låt oss se vad dessa värden motsvarar som vi kan observera i vår EXPLAIN.

  • Den första parametern visar operationen som motorn utför på data i detta steg.
  • Beräknad startkostnad. Detta är den tid som går åt innan utgångsfasen kan börja.
  • Uppskattad total kostnad. Detta anges under antagandet att plannoden är färdigkörd. I praktiken kan en nods överordnade nod sluta läsa alla tillgängliga rader.
  • Uppskattat antal rader som matas ut av denna plannod. Återigen antas noden köras tills den är klar.
  • Uppskattad genomsnittlig bredd på rader som matas ut av denna plannod.

Den mest kritiska delen av displayen är den uppskattade kostnaden för utförande av uttalandet, vilket är planerarens gissning på hur lång tid det kommer att ta att köra uttalandet. När vi jämför hur effektiv den ena frågan är mot den andra kommer vi i praktiken att jämföra kostnadsvärdena för dem.

Det är viktigt att förstå att kostnaden för en nod på översta nivån inkluderar kostnaden för alla dess undernoder. Det är också viktigt att inse att kostnaden bara återspeglar saker som planeraren bryr sig om. I synnerhet tar kostnaden inte hänsyn till den tid som går åt till att överföra resultatrader till klienten, vilket kan vara en viktig faktor i den verkliga förflutna tiden; men planeraren ignorerar den eftersom den inte kan ändra den genom att ändra planen.

Kostnaderna mäts i godtyckliga enheter som bestäms av planerarens kostnadsparametrar. Traditionell praxis är att mäta kostnaderna i enheter för hämtning av disksidor; det vill säga seq_page_cost är konventionellt satt till 1.0 och de andra kostnadsparametrarna är inställda i förhållande till det.

FÖRKLARA ANALYS

Med det här alternativet exekverar EXPLAIN frågan och visar sedan det sanna antal rader och verklig körtid som ackumulerats inom varje plannod, tillsammans med samma uppskattningar som en vanlig EXPLAIN visar.

Låt oss se ett exempel på hur det här verktyget används.

world=# EXPLAIN ANALYZE SELECT * FROM city t1,country t2 WHERE id>100 AND t1.population>700000 AND t2.population<7000000;
                                                     QUERY PLAN                                                      
----------------------------------------------------------------------------------------------------------------------
Nested Loop  (cost=0.00..734.81 rows=50662 width=144) (actual time=0.081..22.066 rows=51100 loops=1)
  ->  Seq Scan on city t1  (cost=0.00..93.19 rows=347 width=31) (actual time=0.069..0.618 rows=350 loops=1)
        Filter: ((id > 100) AND (population > 700000))
        Rows Removed by Filter: 3729
  ->  Materialize  (cost=0.00..8.72 rows=146 width=113) (actual time=0.000..0.011 rows=146 loops=350)
        ->  Seq Scan on country t2  (cost=0.00..7.99 rows=146 width=113) (actual time=0.007..0.058 rows=146 loops=1)
              Filter: (population < 7000000)
              Rows Removed by Filter: 93
Planning time: 0.136 ms
Execution time: 24.627 ms
(10 rows)

Om vi ​​inte hittar anledningen till att våra frågor tar längre tid än de borde kan vi kolla in den här bloggen för mer information.

VAKUUM

VACUUM-processen är ansvarig för flera underhållsuppgifter i databasen, en av dem återställer lagring som upptas av döda tuplar. I den normala driften av PostgreSQL tas tupler som raderas eller föråldras av en uppdatering inte fysiskt bort från deras tabell; de förblir närvarande tills ett VAKUUM utförs. Därför är det nödvändigt att göra VAKUUM med jämna mellanrum, särskilt i ofta uppdaterade tabeller.

Om VAKUUMET tar för mycket tid eller resurser, betyder det att vi måste göra det oftare, så att varje operation har mindre att rengöra.

I vilket fall som helst kan du behöva inaktivera VACUUM, till exempel när du laddar data i stora mängder.

VAKUUMET återvinner helt enkelt utrymme och gör det tillgängligt för återanvändning. Denna form av kommandot kan fungera parallellt med normal läsning och skrivning av tabellen, eftersom ett exklusivt lås inte erhålls. Det extra utrymmet returneras dock inte till operativsystemet (i de flesta fall); den är endast tillgänglig för återanvändning inom samma tabell.

VACUUM FULL skriver om allt innehåll i tabellen i en ny diskfil utan extra utrymme, vilket gör att det oanvända utrymmet kan återgå till operativsystemet. Detta formulär är mycket långsammare och kräver ett exklusivt lås på varje bord under bearbetning.

VAKUUMANALYS utför en VAKUUM och sedan en ANALYS för varje vald tabell. Detta är ett praktiskt sätt att kombinera rutinunderhållsskript.

ANALYSE samlar in statistik om innehållet i tabellerna i databasen och lagrar resultaten i pg_statistic. Därefter använder frågeplaneraren denna statistik för att bestämma de mest effektiva exekveringsplanerna för frågor.

Ladda ner Whitepaper Today PostgreSQL Management &Automation med ClusterControlLäs om vad du behöver veta för att distribuera, övervaka, hantera och skala PostgreSQLDladda Whitepaper

Konfigurationsparametrar

För att ändra dessa parametrar måste vi redigera filen $ PGDATA / postgresql.conf. Vi måste komma ihåg att vissa av dem kräver en omstart av vår databas.

max_anslutningar

Bestämmer det maximala antalet samtidiga anslutningar till vår databas. Det finns minnesresurser som kan konfigureras per klient, därför kan det maximala antalet klienter föreslå den maximala mängden minne som används.

superuser_reserved_connections

Om gränsen för max_connection nås, är dessa anslutningar reserverade för superanvändare.

delade_buffertar

Ställer in mängden minne som databasservern använder för delade minnesbuffertar. Om du har en dedikerad databasserver med 1 GB eller mer RAM, är ett rimligt initialvärde för shared_buffers 25 % av systemets minne. Större konfigurationer för shared_buffers kräver i allmänhet en motsvarande ökning av max_wal_size, för att förlänga processen att skriva stora mängder ny eller modifierad data över en längre tidsperiod.

temp_buffertar

Ställer in det maximala antalet tillfälliga buffertar som används för varje session. Dessa är lokala sessionsbuffertar som endast används för att komma åt tillfälliga tabeller. En session kommer att tilldela de temporära buffertarna efter behov upp till gränsen som ges av temp_buffers.

work_mem

Anger mängden minne som kommer att användas av de interna funktionerna för ORDER BY, DISTINCT, JOIN och hashtabeller innan skrivning till de temporära filerna på disken. När vi konfigurerar detta värde måste vi ta hänsyn till att flera sessioner utför dessa operationer samtidigt och varje operation kommer att tillåtas använda så mycket minne som specificeras av detta värde innan den börjar skriva data i temporära filer.

Det här alternativet kallades sort_mem i äldre versioner av PostgreSQL.

maintenance_work_mem

Anger den maximala mängden minne som underhållsoperationer kommer att använda, som VACUUM, CREATE INDEX och ALTER TABLE ADD FOREIGN KEY. Eftersom endast en av dessa operationer kan utföras samtidigt av en session, och en installation vanligtvis inte har många av dem som körs samtidigt, kan den vara större än work_mem. Större konfigurationer kan förbättra prestandan för VACUUM och databasåterställningar.

När autovacuum exekveras kan detta minne tilldelas antalet gånger som parametern autovacuum_max_workers är konfigurerad, så vi måste ta hänsyn till detta, eller på annat sätt konfigurera parametern autovacuum_work_mem för att hantera detta separat.

fsync

Om fsync är aktiverat kommer PostgreSQL att försöka se till att uppdateringarna skrivs fysiskt till disken. Detta säkerställer att databasklustret kan återställas till ett konsekvent tillstånd efter ett operativsystem eller en maskinvarukrasch.

Även om inaktivering av fsync i allmänhet förbättrar prestandan, kan det orsaka dataförlust i händelse av ett strömavbrott eller en systemkrasch. Därför är det bara att rekommendera att inaktivera fsync om du enkelt kan återskapa hela din databas från extern data.

checkpoint_segments (PostgreSQL <9.5)

Maximalt antal postfilsegment mellan automatiska WAL-kontrollpunkter (varje segment är normalt 16 megabyte). Att öka denna parameter kan öka den tid som krävs för att återställa fel. I ett system med mycket trafik kan det påverka prestandan om det är satt till ett mycket lågt värde. Det rekommenderas att öka värdet av checkpoint_segments på system med många datamodifieringar.

En bra praxis är också att spara WAL-filerna på en annan disk än PGDATA. Detta är användbart både för att balansera skrivandet och för säkerheten vid hårdvarufel.

Från och med PostgreSQL 9.5 togs konfigurationsvariabeln "checkpoint_segments" bort och ersattes med "max_wal_size" och "min_wal_size"

max_wal_size (PostgreSQL>=9.5)

Maximal storlek som WAL får växa mellan kontrollpunkterna. Storleken på WAL kan överstiga max_wal_size under speciella omständigheter. Att öka denna parameter kan öka den tid som krävs för att återställa fel.

min_wal_size (PostgreSQL>=9.5)

När WAL-filen hålls under detta värde återvinns den för framtida användning vid en kontrollpunkt, istället för att raderas. Detta kan användas för att säkerställa att tillräckligt med WAL-utrymme reserveras för att hantera toppar i användningen av WAL, till exempel när man utför stora batch-jobb.

wal_sync_method

Metod som används för att tvinga fram WAL-uppdateringar till disken. Om fsync är inaktiverat har denna inställning ingen effekt.

wal_buffertar

Mängden delat minne som används för WAL-data som ännu inte har skrivits till disken. Standardinställningen är cirka 3 % av shared_buffers, inte mindre än 64KB eller mer än storleken på ett WAL-segment (vanligtvis 16MB). Om du ställer in detta värde till minst några MB kan det förbättra skrivprestandan på en server med många samtidiga transaktioner.

effective_cache_size

Detta värde används av frågeplaneraren för att ta hänsyn till planer som kanske inte får plats i minnet. Detta beaktas i kostnadsuppskattningarna för att använda ett index; ett högt värde gör det mer sannolikt att indexskanningar används och ett lågt värde gör det mer sannolikt att sekventiella skanningar kommer att användas. Ett rimligt värde skulle vara 50 % av RAM-minnet.

default_statistics_target

PostgreSQL samlar in statistik från var och en av tabellerna i sin databas för att bestämma hur frågor ska köras på dem. Som standard samlar den inte in för mycket information, och om du inte får bra exekveringsplaner bör du öka detta värde och sedan köra ANALYSE i databasen igen (eller vänta på AUTOVAKUUM).

synchronous_commit

Anger om transaktionsbekräftelsen kommer att vänta på att WAL-posterna skrivs till disken innan kommandot returnerar en "framgång"-indikation till klienten. De möjliga värdena är:"on", "remote_apply", "remote_write", "local" och "off". Standardinställningen är "på". När den är inaktiverad kan det uppstå en fördröjning mellan det att klienten kommer tillbaka och när transaktionen garanterat är säker mot ett serverlås. Till skillnad från fsync skapar inte inaktivering av denna parameter någon risk för databasinkonsekvens:en krasch av operativsystemet eller databasen kan resultera i förlust av några nyligen genomförda transaktioner, men tillståndet för databasen kommer att vara exakt detsamma som om dessa transaktioner hade avbokats rent. Därför kan inaktivering av synchronous_commit vara ett användbart alternativ när prestanda är viktigare än den exakta säkerheten om en transaktions hållbarhet.

Loggning

Det finns flera typer av data att logga som kan vara användbara eller inte. Låt oss se några av dem:

  • log_min_error_statement:Anger den lägsta loggningsnivån.
  • log_min_duration_statement:Används för att registrera långsamma frågor i systemet.
  • log_line_prefix:Följer informationen i början av varje loggrad.
  • log_statement:Du kan välja mellan NONE, DDL, MOD, ALL. Att använda "alla" kan orsaka prestandaproblem.

Design

I många fall kan utformningen av vår databas påverka prestandan. Vi måste vara försiktiga i vår design, normalisera vårt schema och undvika överflödig data. I många fall är det bekvämt att ha flera små bord istället för ett stort bord. Men som vi sa tidigare beror allt på vårt system och det finns inte en enda möjlig lösning.

Vi måste också använda indexen på ett ansvarsfullt sätt. Vi bör inte skapa index för varje fält eller kombination av fält, eftersom vi, även om vi inte behöver resa hela tabellen, använder diskutrymme och lägger till overhead för skrivoperationer.

Ett annat mycket användbart verktyg är hanteringen av anslutningspoolen. Om vi ​​har ett system med mycket belastning kan vi använda detta för att undvika att anslutningarna i databasen mättas och för att kunna återanvända dem.

Hårdvara

Som vi nämnde i början av den här bloggen är hårdvara en av de viktiga faktorerna som direkt påverkar prestandan för vår databas. Låt oss se några punkter att tänka på.

  • Minne:Ju mer RAM vi har, desto mer minnesdata kan vi hantera, och det betyder bättre prestanda. Hastigheten för att skriva och läsa på disk är mycket långsammare än i minnet, därför, ju mer information vi kan ha i minnet, desto bättre prestanda får vi.
  • CPU:Det kanske inte är så meningsfullt att säga detta, men ju mer CPU vi har, desto bättre. Det är i alla fall inte det viktigaste när det gäller hårdvara, men om vi kan ha en bra CPU kommer vår processorkapacitet att förbättras och det påverkar vår databas direkt.
  • Hårddisk:Vi har flera typer av skivor som vi kan använda, SCSI, SATA, SAS, IDE. Vi har även solid state-diskar. Vi måste jämföra kvalitet / pris, som vi bör använda för att jämföra dess hastighet. Men typen av disk är inte det enda att tänka på, vi måste också se hur man konfigurerar dem. Om vi ​​vill ha bra prestanda kan vi använda RAID10 och behålla WALs på en annan disk utanför RAID. Det rekommenderas inte att använda RAID5 eftersom prestandan för denna typ av RAID för databaser inte är bra.

Slutsats

Efter att ha tagit hänsyn till punkterna som nämns i den här bloggen kan vi utföra ett riktmärke för att verifiera databasens beteende.

Det är också viktigt att ha vår databas övervakad för att avgöra om vi står inför ett prestandaproblem och för att kunna lösa det så snart som möjligt. För denna uppgift finns det flera verktyg som Nagios, ClusterControl eller Zabbix, bland andra, som gör att vi inte bara kan övervaka, utan med några av dem, låter oss vidta proaktiva åtgärder innan problemet uppstår. Med ClusterControl kan vi, förutom övervakning, administration och flera andra verktyg, få rekommendationer om vilka åtgärder vi kan vidta när vi får prestandavarningar. Detta gör att vi kan ha en uppfattning om hur vi ska lösa potentiella problem.

Den här bloggen är inte avsedd att vara en uttömmande guide till hur man kan förbättra databasprestanda. Förhoppningsvis ger det en tydligare bild av vilka saker som kan bli viktiga och några av de grundläggande parametrarna som kan konfigureras. Tveka inte att meddela oss om vi har missat några viktiga.


  1. Hur man konverterar en sträng till gemener i SQL

  2. Division av heltal ger 0

  3. Nästan noll driftstopp automatiska uppgraderingar av PostgreSQL-kluster i moln (del II)

  4. Logga in med externa tjänster