Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Rapporten presenterar nÄgra tillvÀgagÄngssÀtt som tillÄter övervaka prestanda för SQL-frÄgor nÀr det finns miljontals av dem per dag, och det finns hundratals övervakade PostgreSQL-servrar.

Vilka tekniska lösningar gör att vi effektivt kan bearbeta en sÄdan mÀngd information, och hur underlÀttar detta livet för en vanlig utvecklare?


Vem Àr intresserad? analys av specifika problem och olika optimeringstekniker SQL-frÄgor och lösa typiska DBA-problem i PostgreSQL - du kan ocksÄ lÀsa en serie artiklar om detta Àmne.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)
Jag heter Kirill Borovikov, jag representerar Tensorföretag. Specifikt Àr jag specialiserad pÄ att arbeta med databaser i vÄrt företag.

Idag kommer jag att berÀtta hur vi optimerar frÄgor, nÀr du inte behöver "plocka isÀr" prestandan för en enda frÄga, utan lösa problemet en masse. NÀr det finns miljontals förfrÄgningar och du behöver hitta nÄgra tillvÀgagÄngssÀtt till lösning detta stora problem.

I allmÀnhet Àr Tensor för en miljon av vÄra kunder VLSI Àr vÄr applikation: företags sociala nÀtverk, lösningar för videokommunikation, för internt och externt dokumentflöde, redovisningssystem för redovisning och lager,... Det vill sÀga en sÄdan "megakombination" för integrerad företagsledning, dÀr det finns mer Àn 100 olika interna projekt.

För att sÀkerstÀlla att de alla fungerar och utvecklas normalt har vi 10 utvecklingscentra över hela landet, med fler i dem 1000 utvecklare.

Vi har arbetat med PostgreSQL sedan 2008 och har samlat pÄ oss en stor mÀngd av det vi bearbetar - kunddata, statistisk, analytisk, data frÄn externa informationssystem - mer Àn 400TB. Det finns cirka 250 servrar enbart i produktion och totalt Àr det cirka 1000 databasservrar som vi övervakar.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

SQL Àr ett deklarativt sprÄk. Du beskriver inte "hur" nÄgot ska fungera, utan "vad" du vill uppnÄ. DBMS vet bÀttre hur man gör en JOIN - hur man kopplar ihop dina tabeller, vilka villkor som ska införas, vad kommer att gÄ igenom indexet, vad kommer inte ...

Vissa DBMS:er accepterar tips: "Nej, anslut dessa tvÄ tabeller i en sÄdan och en sÄdan kö", men PostgreSQL kan inte göra detta. Detta Àr ledande utvecklares medvetna position: "Vi skulle hellre avsluta frÄgeoptimeraren Àn att lÄta utvecklare anvÀnda nÄgon form av tips."

Men trots att PostgreSQL inte tillÄter "utsidan" att kontrollera sig sjÀlv, tillÄter det perfekt se vad som hÀnder inom honomnÀr du kör din frÄga och dÀr den har problem.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

I allmĂ€nhet, vilka klassiska problem brukar en utvecklare [till en DBA] komma med? ”HĂ€r uppfyllde vi begĂ€ran, och allt gĂ„r lĂ„ngsamt med oss, allt hĂ€nger, nĂ„got hĂ€nder... NĂ„got slags problem!”

Anledningarna Àr nÀstan alltid desamma:

  • ineffektiv frĂ„gealgoritm
    Utvecklare: "Nu ger jag honom 10 tabeller i SQL via JOIN..." - och förvÀntar sig att hans villkor mirakulöst kommer att vara effektivt "obundna" och att han kommer att fÄ allt snabbt. Men mirakel sker inte, och alla system med sÄdan variation (10 tabeller i en FROM) ger alltid nÄgot slags fel. [artikel]
  • förĂ„ldrad statistik
    Denna punkt Àr mycket relevant specifikt för PostgreSQL, nÀr du "hÀllde" en stor datauppsÀttning pÄ servern, gör en förfrÄgan och den "sexcaniterar" din surfplatta. För igÄr fanns det 10 poster i den, och idag finns det 10 miljoner, men PostgreSQL Àr Ànnu inte medveten om detta, och vi mÄste berÀtta om det. [artikel]
  • "plugga" pĂ„ resurser
    Du har installerat en stor och hÄrt belastad databas pÄ en svag server som inte har tillrÀckligt med disk-, minnes- eller processorprestanda. Och det Àr allt... NÄgonstans finns det ett prestationstak över vilket du inte lÀngre kan hoppa.
  • blockering
    Detta Àr en svÄr punkt, men de Àr mest relevanta för olika modifieringsfrÄgor (INSERT, UPDATE, DELETE) - detta Àr ett separat stort Àmne.

FĂ„r en plan

...Och för allt annat vi behöver en plan! Vi mÄste se vad som hÀnder inuti servern.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

En frÄgeexekveringsplan för PostgreSQL Àr ett trÀd för frÄgeexekveringsalgoritmen i textrepresentation. Det Àr just algoritmen som, som ett resultat av analys av planeraren, visade sig vara den mest effektiva.

Varje trÀdnod Àr en operation: hÀmta data frÄn en tabell eller index, bygga en bitmapp, sammanfoga tvÄ tabeller, sammanfoga, skÀra eller utesluta urval. Att köra en frÄga innebÀr att gÄ igenom noderna i detta trÀd.

För att fÄ frÄgeplanen Àr det enklaste sÀttet att köra uttalandet EXPLAIN. För att fÄ med alla verkliga attribut, det vill sÀga att faktiskt köra en frÄga pÄ basen - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Den dĂ„liga delen: nĂ€r du kör det hĂ€nder det "hĂ€r och nu", sĂ„ det Ă€r bara lĂ€mpligt för lokal felsökning. Om du tar en högt laddad server som Ă€r under ett starkt flöde av dataförĂ€ndringar, och du ser: "Åh! HĂ€r har vi ett lĂ„ngsamt utförandeXia begĂ€ran." För en halvtimme, en timme sedan - medan du körde och hĂ€mtade denna begĂ€ran frĂ„n loggarna, förde den tillbaka till servern, Ă€ndrades hela din datauppsĂ€ttning och statistik. Du kör det för att felsöka - och det gĂ„r snabbt! Och du kan inte förstĂ„ varför, varför Đ±Ń‹Đ»ĐŸ lĂ„ngsamt.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

För att förstÄ vad som hÀnde exakt i det ögonblick dÄ begÀran kördes pÄ servern, skrev smarta personer auto_explain-modul. Det finns i nÀstan alla de vanligaste PostgreSQL-distributionerna och kan enkelt aktiveras i konfigurationsfilen.

Om den inser att en begÀran löper lÀngre Àn grÀnsen du sa till den, gör den det "ögonblicksbild" av planen för denna begÀran och skriver dem tillsammans i loggen.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Allt verkar vara bra nu, vi gÄr till stocken och ser dÀr... [text fotduk]. Men vi kan inte sÀga nÄgot om det, annat Àn att det Àr en utmÀrkt plan eftersom det tog 11 ms att genomföra.

Allt verkar vara bra - men ingenting Àr klart vad som faktiskt hÀnde. Förutom den allmÀnna tiden ser vi egentligen ingenting. För att titta pÄ ett sÄdant "lamm" av vanlig text Àr i allmÀnhet inte visuellt.

Men Àven om det inte Àr uppenbart, Àven om det Àr obekvÀmt, finns det mer grundlÀggande problem:

  • Noden indikerar summan av resurser för hela undertrĂ€det under honom. Det vill sĂ€ga, du kan inte bara ta reda pĂ„ hur mycket tid som spenderades pĂ„ just denna Index Scan om det finns nĂ„got kapslat tillstĂ„nd under den. Vi mĂ„ste dynamiskt titta för att se om det finns "barn" och villkorsvariabler, CTEs inuti - och subtrahera allt detta "i vĂ„ra sinnen".
  • Andra punkten: tiden som anges pĂ„ noden Ă€r exekveringstid för en enda nod. Om denna nod exekveras som ett resultat av, till exempel, en loop genom tabellposter flera gĂ„nger, sĂ„ ökar antalet loopar – cykler för denna nod – i planen. Men sjĂ€lva atomavrĂ€ttningstiden förblir densamma nĂ€r det gĂ€ller planen. Det vill sĂ€ga, för att förstĂ„ hur lĂ€nge denna nod utfördes totalt, mĂ„ste du multiplicera en sak med en annan - igen, "i ditt huvud."

I sÄdana situationer, förstÄ "Vem Àr den svagaste lÀnken?" nÀstan omöjligt. DÀrför skriver Àven utvecklarna sjÀlva i "manualen" att "Att förstÄ en plan Àr en konst som mÄste lÀras, uppleva...".

Men vi har 1000 utvecklare, och du kan inte förmedla den hĂ€r upplevelsen till var och en av dem. Jag, du, han vet, men nĂ„gon dĂ€r borta vet inte lĂ€ngre. Kanske kommer han att lĂ€ra sig, eller kanske inte, men han behöver jobba nu – och var skulle han fĂ„ den hĂ€r erfarenheten?

Planera visualisering

DÀrför insÄg vi att för att kunna hantera dessa problem behöver vi bra visualisering av planen. [artikel]

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Vi gick först "genom marknaden" - lÄt oss titta pÄ Internet för att se vad som ens existerar.

Men det visade sig att det finns vÀldigt fÄ relativt "live" lösningar som Àr mer eller mindre under utveckling - bokstavligen bara en: explain.depesz.com av Hubert Lubaczewski. NÀr du anger "flöde"-fÀltet en textrepresentation av planen, visar den dig en tabell med analyserad data:

  • nodens egen handlĂ€ggningstid
  • total tid för hela undertrĂ€det
  • antal poster som hĂ€mtats som var statistiskt förvĂ€ntade
  • sjĂ€lva nodkroppen

Den hÀr tjÀnsten har ocksÄ möjlighet att dela ett arkiv med lÀnkar. Du slÀngde in din plan och sa: "Hej, Vasya, hÀr Àr en lÀnk, det Àr nÄgot fel dÀr."

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Men det finns ocksÄ smÄ problem.

För det första en enorm mÀngd "copy-paste". Du tar en bit av stocken, sticker in den dÀr, och igen, och igen.

Andra, ingen analys av mĂ€ngden avlĂ€st data — samma buffertar som matas ut EXPLAIN (ANALYZE, BUFFERS), vi ser det inte hĂ€r. Han vet helt enkelt inte hur man tar isĂ€r dem, förstĂ„r dem och arbetar med dem. NĂ€r du lĂ€ser mycket data och inser att du kan ha felallokerat disken och minnescachen, Ă€r denna information mycket viktig.

Den tredje negativa punkten Ă€r den mycket svaga utvecklingen av detta projekt. Åtagandena Ă€r mycket smĂ„, det Ă€r bra om det Ă€r en gĂ„ng var sjĂ€tte mĂ„nad, och koden finns i Perl.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Men det hÀr Àr alla "texter", vi skulle pÄ nÄgot sÀtt kunna leva med detta, men det finns en sak som kraftigt avvisade oss frÄn den hÀr tjÀnsten. Dessa Àr fel i analysen av Common Table Expression (CTE) och olika dynamiska noder som InitPlan/SubPlan.

Om du tror pÄ den hÀr bilden Àr den totala exekveringstiden för varje enskild nod större Àn den totala exekveringstiden för hela begÀran. Det Àr enkelt - genereringstiden för denna CTE subtraherades inte frÄn CTE Scan-noden. DÀrför vet vi inte lÀngre det korrekta svaret pÄ hur lÄng tid sjÀlva CTE-skanningen tog.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

DĂ„ insĂ„g vi att det var dags att skriva eget – hurra! Varje utvecklare sĂ€ger: "Nu ska vi skriva vĂ„rt eget, det blir superenkelt!"

Vi tog en stack typisk för webbtjÀnster: en kÀrna baserad pÄ Node.js + Express, anvÀnde Bootstrap och D3.js för vackra diagram. Och vÄra förvÀntningar var helt berÀttigade - vi fick den första prototypen pÄ 2 veckor:

  • anpassad planparser
    Det vill sÀga, nu kan vi analysera vilken plan som helst frÄn de som genereras av PostgreSQL.
  • korrekt analys av dynamiska noder - CTE Scan, InitPlan, SubPlan
  • analys av buffertfördelning - var datasidor lĂ€ses frĂ„n minnet, var frĂ„n den lokala cachen, var frĂ„n disken
  • fick klarhet
    För att inte "grÀva" allt detta i loggen, utan för att se den "svagaste lÀnken" direkt i bilden.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Vi fick nÄgot sÄnt hÀr, med syntaxmarkering inkluderad. Men vanligtvis arbetar vÄra utvecklare inte lÀngre med en fullstÀndig representation av planen, utan med en kortare. Vi har trots allt redan analyserat alla siffror och kastat dem Ät vÀnster och höger, och i mitten lÀmnade vi bara den första raden, vilken typ av nod det Àr: CTE Scan, CTE generation eller Seq Scan enligt nÄgot tecken.

Detta Àr den förkortade representationen vi kallar plan mall.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Vad mer skulle vara bekvÀmt? Det skulle vara bekvÀmt att se vilken del av vÄr totala tid som allokeras till vilken nod - och bara "hÄlla den" Ät sidan tÄrtdiagram.

Vi pekar pÄ noden och ser - det visar sig att Seq Scan tog mindre Àn en fjÀrdedel av den totala tiden, och de ÄterstÄende 3/4 togs av CTE Scan. SkrÀck! Detta Àr en liten anteckning om "brandhastigheten" för CTE Scan om du aktivt anvÀnder dem i dina frÄgor. De Àr inte sÀrskilt snabba - de Àr sÀmre Àven för vanlig bordsskanning. [artikel] [artikel]

Men vanligtvis Àr sÄdana diagram mer intressanta, mer komplexa, nÀr vi omedelbart pekar pÄ ett segment och till exempel ser att mer Àn hÀlften av tiden nÄgon Seq Scan "Àtit". Dessutom fanns det nÄgot slags filter inuti, mÄnga poster slÀngdes enligt det... Du kan direkt kasta den hÀr bilden till utvecklaren och sÀga: "Vasya, allt Àr dÄligt hÀr för dig! Ta reda pÄ det, titta - nÄgot Àr fel!"

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Naturligtvis var det nÄgra "rakes" inblandade.

Det första vi stötte pĂ„ var avrundningsproblemet. Tiden för varje enskild nod i planen anges med en noggrannhet pĂ„ 1 ÎŒs. Och nĂ€r antalet nodcykler överstiger till exempel 1000 - efter exekvering av PostgreSQL delat "inom noggrannhet", dĂ„ fĂ„r vi vid tillbakarĂ€kning den totala tiden "nĂ„gonstans mellan 0.95 ms och 1.05 ms". NĂ€r rĂ€kningen gĂ„r till mikrosekunder Ă€r det okej, men nĂ€r det redan Ă€r [milli]sekunder, mĂ„ste du ta hĂ€nsyn till denna information nĂ€r du "löser" resurser till noderna i planen "vem konsumerade hur mycket".

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Den andra punkten, mer komplex, Àr fördelningen av resurser (dessa buffertar) mellan dynamiska noder. Detta kostade oss de första 2 veckorna av prototypen plus ytterligare 4 veckor.

Det Àr ganska lÀtt att fÄ den hÀr typen av problem - vi gör en CTE och lÀr lÀsa nÄgot i den. Faktum Àr att PostgreSQL Àr "smart" och kommer inte att lÀsa nÄgot direkt dÀr. Sedan tar vi den första skivan frÄn den, och till den den hundraförsta frÄn samma CTE.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Vi tittar pÄ planen och förstÄr - det Àr konstigt, vi har 3 buffertar (datasidor) "konsumerade" i Seq Scan, 1 till i CTE Scan och 2 till i den andra CTE Scan. Det vill sÀga om vi helt enkelt summerar allt sÄ fÄr vi 6, men frÄn surfplattan lÀser vi bara 3! CTE Scan lÀser ingenting frÄn nÄgonstans, utan arbetar direkt med processminnet. Det vill sÀga nÄgot Àr helt klart fel hÀr!

Det visar sig faktiskt att hÀr Àr alla de dÀr 3 sidorna med data som begÀrdes frÄn Seq Scan, först 1 bad om den 1:a CTE Scan, och sedan den 2:a, och 2 till lÀstes för honom. 3 sidor lÀstes data, inte 6.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Och den hÀr bilden ledde oss till förstÄelsen att genomförandet av en plan inte lÀngre Àr ett trÀd, utan bara nÄgon form av acyklisk graf. Och vi fick ett diagram som detta, sÄ att vi förstÄr "vad som kom ifrÄn var frÄn början." Det vill sÀga, hÀr skapade vi en CTE frÄn pg_class, och bad om den tvÄ gÄnger, och nÀstan all vÄr tid spenderades pÄ grenen nÀr vi bad om den andra gÄngen. Det Àr klart att det Àr mycket dyrare att lÀsa den 2:a posten Àn att bara lÀsa den 101:a posten frÄn surfplattan.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Vi andades ut en stund. De sa: "Nu, Neo, du vet kung fu! Nu Àr vÄr upplevelse direkt pÄ din skÀrm. Nu kan du anvÀnda den." [artikel]

Loggkonsolidering

VÄra 1000 utvecklare andades en lÀttnadens suck. Men vi förstod att vi bara har hundratals "combat"-servrar, och allt detta "copy-paste" frÄn utvecklarnas sida Àr inte alls bekvÀmt. Vi insÄg att vi var tvungna att hÀmta den sjÀlva.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Generellt finns det en standardmodul som kan samla in statistik, men den mÄste ocksÄ aktiveras i konfigurationen - detta modul pg_stat_statements. Men han passade inte oss.

För det första tilldelar den samma frÄgor med olika scheman inom samma databas olika QueryIds. Det vill sÀga om du först gör det SET search_path = '01'; SELECT * FROM user LIMIT 1;och sedan SET search_path = '02'; och samma förfrÄgan, dÄ kommer statistiken för denna modul att ha olika poster, och jag kommer inte att kunna samla in allmÀn statistik specifikt i samband med denna förfrÄgningsprofil, utan att ta hÀnsyn till scheman.

Den andra punkten som hindrade oss frÄn att anvÀnda den Àr brist pÄ planer. Det vill sÀga det finns ingen plan, det finns bara sjÀlva förfrÄgan. Vi ser vad som saktade ner, men vi förstÄr inte varför. Och hÀr ÄtervÀnder vi till problemet med ett snabbt förÀnderligt dataset.

Och sista stunden - brist pĂ„ "fakta". Det vill sĂ€ga, du kan inte ta itu med en specifik instans av sökexekvering - det finns ingen, det finns bara aggregerad statistik. Även om det gĂ„r att jobba med detta Ă€r det bara vĂ€ldigt svĂ„rt.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

DÀrför bestÀmde vi oss för att bekÀmpa copy-paste och började skriva kollektor.

Samlaren ansluter via SSH, upprÀttar en sÀker anslutning till servern med databasen med hjÀlp av ett certifikat, och tail -F "klamrar sig fast" vid den i loggfilen. SÄ i denna session vi fÄr en komplett "spegel" av hela loggfilen, som servern genererar. Belastningen pÄ sjÀlva servern Àr minimal, eftersom vi inte analyserar nÄgonting dÀr, vi speglar bara trafiken.

Eftersom vi redan hade börjat skriva grÀnssnittet i Node.js fortsatte vi att skriva samlaren i det. Och den hÀr tekniken har motiverat sig, eftersom det Àr vÀldigt bekvÀmt att anvÀnda JavaScript för att arbeta med svagt formaterad textdata, som Àr loggen. Och sjÀlva Node.js-infrastrukturen som en backend-plattform lÄter dig enkelt och bekvÀmt arbeta med nÀtverksanslutningar, och faktiskt med alla dataströmmar.

Följaktligen "strÀcker" vi tvÄ anslutningar: den första för att "lyssna" pÄ sjÀlva loggen och ta den till oss sjÀlva, och den andra för att regelbundet frÄga basen. "Men loggen visar att skylten med oid 123 Àr blockerad," men detta betyder ingenting för utvecklaren, och det skulle vara trevligt att frÄga databasen, "Vad Àr OID = 123 ÀndÄ?" Och sÄ frÄgar vi regelbundet basen vad vi Ànnu inte vet om oss sjÀlva.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

"Det finns bara en sak du inte tog hĂ€nsyn till, det finns en art av elefantliknande bin!..." Vi började utveckla det hĂ€r systemet nĂ€r vi ville övervaka 10 servrar. Det mest kritiska i vĂ„r förstĂ„else, dĂ€r det uppstod nĂ„gra problem som var svĂ„ra att hantera. Men under det första kvartalet fick vi hundra för övervakning – eftersom systemet fungerade, alla ville ha det, alla var bekvĂ€ma.

Allt detta mÄste lÀggas ihop, dataflödet Àr stort och aktivt. Faktum Àr att det vi övervakar, vad vi kan hantera, Àr vad vi anvÀnder. Vi anvÀnder Àven PostgreSQL som datalagring. Och ingenting Àr snabbare att "hÀlla" data i den Àn operatören COPY Inte Àn.

Men att helt enkelt "hÀlla" data Àr inte riktigt vÄr teknik. För om du har ungefÀr 50 100 förfrÄgningar per sekund pÄ hundra servrar, kommer detta att generera 150-XNUMX GB loggar per dag. DÀrför var vi tvungna att försiktigt "klippa" basen.

För det första gjorde vi det uppdelning per dag, eftersom ingen i stort sett Àr intresserad av sambandet mellan dagar. Vilken skillnad gör det vad du hade igÄr, om du ikvÀll rullade ut en ny version av applikationen - och redan lite ny statistik.

För det andra lÀrde vi oss (var tvingade) vÀldigt, vÀldigt snabbt att skriva med hjÀlp av COPY. Det vill sÀga inte bara COPYeftersom han Àr snabbare Àn INSERT, och Ànnu snabbare.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Den tredje punkten - jag var tvungen överge utlösare respektive frÀmmande nycklar. Det vill sÀga att vi inte har nÄgon referensintegritet alls. För om du har en tabell som har ett par FK:er, och du sÀger i databasstrukturen att "hÀr Àr en loggpost som t.ex. refereras av FK till en grupp av poster", sÄ nÀr du infogar den, PostgreSQL har inget kvar Àn hur man tar det och gör det Àrligt SELECT 1 FROM master_fk1_table WHERE ... med identifieraren som du försöker infoga - bara för att kontrollera att denna post finns dÀr, att du inte "bryter av" denna frÀmmande nyckel med din insÀttning.

IstÀllet för en post till mÄltabellen och dess index fÄr vi den extra fördelen av att lÀsa frÄn alla tabeller den refererar till. Men vi behöver inte det hÀr alls - vÄr uppgift Àr att spela in sÄ mycket som möjligt och sÄ snabbt som möjligt med minsta möjliga belastning. SÄ FK - ner!

NÀsta punkt Àr aggregering och hash. Till en början implementerade vi dem i databasen - trots allt Àr det bekvÀmt att omedelbart, nÀr en post kommer, göra det i nÄgon form av surfplatta "plus ett" direkt i avtryckaren. Tja, det Àr bekvÀmt, men samma dÄliga sak - du infogar en post, men tvingas lÀsa och skriva nÄgot annat frÄn en annan tabell. Dessutom lÀser och skriver du inte bara, du gör det ocksÄ varje gÄng.

FörestÀll dig nu att du har en tabell dÀr du helt enkelt rÀknar antalet förfrÄgningar som har passerat en specifik vÀrd: +1, +1, +1, ..., +1. Och du behöver i princip inte detta - det Àr allt möjligt summa i minnet pÄ samlaren och skicka till databasen pÄ en gÄng +10.

Ja, vid vissa problem kan din logiska integritet "falla isÀr", men detta Àr ett nÀstan orealistiskt fall - eftersom du har en normal server, den har ett batteri i styrenheten, du har en transaktionslogg, en logg pÄ filsystem... I allmÀnhet Àr det inte vÀrt det. Förlusten av produktivitet du fÄr av att köra triggers/FK Àr inte vÀrt kostnaden du Ädrar dig.

Det Àr samma sak med hashing. En viss förfrÄgan flyger till dig, du berÀknar en viss identifierare frÄn den i databasen, skriver den till databasen och berÀttar sedan för alla. Allt Àr bra tills, vid inspelningstillfÀllet, en andra person kommer till dig som vill spela in samma sak - och du blir blockerad, och det hÀr Àr redan dÄligt. DÀrför, om du kan överföra genereringen av vissa ID:n till klienten (i förhÄllande till databasen), Àr det bÀttre att göra detta.

Det var bara perfekt för oss att anvÀnda MD5 frÄn texten - begÀran, plan, mall,... Vi berÀknar det pÄ samlarsidan och "hÀller" det fÀrdiga ID:t i databasen. LÀngden pÄ MD5 och daglig partitionering gör att vi inte kan oroa oss för eventuella kollisioner.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Men för att kunna spela in allt detta snabbt behövde vi modifiera sjÀlva inspelningsproceduren.

Hur brukar du skriva data? Vi har nÄgon slags datauppsÀttning, vi delar upp den i flera tabeller och sedan KOPIERA den - först till den första, sedan till den andra, till den tredje... Det Àr obekvÀmt, eftersom vi verkar skriva en dataström i tre steg sekventiellt. Obehaglig. Kan det göras snabbare? Burk!

För att göra detta rÀcker det bara att bryta ner dessa flöden parallellt med varandra. Det visar sig att vi har fel, förfrÄgningar, mallar, blockeringar, ... som flyger i separata trÄdar - och vi skriver det hela parallellt. TillrÀckligt för detta hÄll en COPY-kanal stÀndigt öppen för varje enskild mÄltabell.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Det vill sÀga hos samlaren det finns alltid en ström, dÀr jag kan skriva de data jag behöver. Men sÄ att databasen ser dessa data, och nÄgon inte fastnar i vÀntan pÄ att denna data ska skrivas, COPY mÄste avbrytas med vissa intervall. För oss var den mest effektiva perioden cirka 100 ms - vi stÀnger den och öppnar den omedelbart igen till samma bord. Och om vi inte har tillrÀckligt med ett flöde under vissa toppar, gör vi poolning upp till en viss grÀns.

Dessutom fick vi reda pÄ att för en sÄdan belastningsprofil Àr all aggregering, nÀr poster samlas in i omgÄngar, dÄlig. Klassisk ondska Àr INSERT ... VALUES och ytterligare 1000 poster. För vid den tidpunkten har du en skrivtopp pÄ media, och alla andra som försöker skriva nÄgot till disken kommer att vÀnta.

För att bli av med sĂ„dana anomalier, samla helt enkelt ingenting, buffra inte alls. Och om buffring till disk intrĂ€ffar (lyckligtvis tillĂ„ter Stream API i Node.js dig att ta reda pĂ„ det) – skjut upp denna anslutning. NĂ€r du fĂ„r en hĂ€ndelse att den Ă€r gratis igen, skriv till den frĂ„n den ackumulerade kön. Och medan det Ă€r upptaget, ta nĂ€sta lediga frĂ„n poolen och skriv till den.

Innan vi introducerade detta tillvÀgagÄngssÀtt för datainspelning hade vi ungefÀr 4K skrivoperationer, och pÄ sÄ sÀtt minskade vi belastningen med 4 gÄnger. Nu har de vuxit ytterligare 6 gÄnger pÄ grund av nya övervakade databaser - upp till 100MB/s. Och nu lagrar vi loggar för de senaste 3 mÄnaderna i en volym pÄ cirka 10-15TB, i hopp om att alla utvecklare pÄ bara tre mÄnader ska kunna lösa alla problem.

Vi förstÄr problemen

Men att helt enkelt samla in all denna data Àr bra, anvÀndbart, relevant, men inte tillrÀckligt - det mÄste förstÄs. För det hÀr Àr miljontals olika planer per dag.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Men miljoner Àr ohanterliga, vi mÄste först göra "mindre". Och först och frÀmst mÄste du bestÀmma hur du ska organisera denna "mindre" sak.

Vi har identifierat tre nyckelpunkter:

  • som skickade denna begĂ€ran
    Det vill sÀga frÄn vilken applikation "kom" den: webbgrÀnssnitt, backend, betalningssystem eller nÄgot annat.
  • dĂ€r det hĂ€nde
    PÄ vilken specifik server? För om du har flera servrar under en applikation, och plötsligt en "blir dum" (eftersom "disken Àr ruttet", "minnet lÀckt", nÄgot annat problem), mÄste du specifikt adressera servern.
  • ĐșĐ°Đș problemet yttrade sig pĂ„ ett eller annat sĂ€tt

För att förstĂ„ "vem" som skickade en förfrĂ„gan till oss anvĂ€nder vi ett standardverktyg - stĂ€ller in en sessionsvariabel: SET application_name = '{bl-host}:{bl-method}'; — vi skickar namnet pĂ„ den affĂ€rslogikvĂ€rd som förfrĂ„gan kommer frĂ„n och namnet pĂ„ metoden eller applikationen som initierade den.

Efter att vi har passerat "Àgaren" av begÀran mÄste den matas ut till loggen - för detta konfigurerar vi variabeln log_line_prefix = ' %m [%p:%v] [%d] %r %a'. För den intresserade kanske titta i manualenvad betyder allt. Det visar sig att vi ser i loggen:

  • tid
  • process- och transaktionsidentifierare
  • Databas namn
  • IP för personen som skickade denna begĂ€ran
  • och metodnamn

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Sedan insÄg vi att det inte Àr sÀrskilt intressant att titta pÄ korrelationen för en begÀran mellan olika servrar. Det Àr inte ofta som du har en situation dÀr en applikation skruvas upp lika hÀr och dÀr. Men Àven om det Àr samma, titta pÄ nÄgon av dessa servrar.

SÄ hÀr Àr snittet "en server - en dag" det visade sig vara tillrÀckligt för oss för alla analyser.

Det första analytiska avsnittet Àr detsamma "prov" - en förkortad form av presentation av planen, rensad frÄn alla numeriska indikatorer. Det andra snittet Àr applikationen eller metoden, och det tredje snittet Àr den specifika plannoden som orsakade oss problem.

NÀr vi gick frÄn specifika instanser till mallar fick vi tvÄ fördelar samtidigt:

  • multipel minskning av antalet objekt för analys
    Vi mÄste analysera problemet inte lÀngre genom tusentals frÄgor eller planer, utan genom dussintals mallar.
  • tidslinjen
    Det vill sÀga, genom att sammanfatta "fakta" inom ett visst avsnitt kan du visa deras utseende under dagen. Och hÀr kan du förstÄ att om du har nÄgot slags mönster som hÀnder, till exempel en gÄng i timmen, men det ska ske en gÄng om dagen, sÄ ska du tÀnka pÄ vad som gick fel - vem som orsakade det och varför, det kanske borde vara hÀr borde inte. Detta Àr en annan icke-numerisk, rent visuell, analysmetod.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

De ÄterstÄende metoderna Àr baserade pÄ de indikatorer som vi extraherar frÄn planen: hur mÄnga gÄnger ett sÄdant mönster intrÀffade, den totala och genomsnittliga tiden, hur mycket data som lÀstes frÄn disken och hur mycket frÄn minnet ...

För att du till exempel kommer till analyssidan för vĂ€rden, titta - nĂ„got börjar lĂ€sa för mycket pĂ„ disken. Disken pĂ„ servern klarar inte av det – vem lĂ€ser frĂ„n den?

Och du kan sortera efter vilken kolumn som helst och bestÀmma vad du ska ta itu med just nu - belastningen pÄ processorn eller disken, eller det totala antalet förfrÄgningar... Vi sorterade det, tittade pÄ de "översta", fixade det och rullade ut en ny version av applikationen.
[videoförelÀsning]

Och direkt kan du se olika applikationer som kommer med samma mall frÄn en förfrÄgan som SELECT * FROM users WHERE login = 'Vasya'. Frontend, backend, bearbetning... Och du undrar varför bearbetning skulle lÀsa anvÀndaren om han inte interagerar med honom.

Det motsatta sÀttet Àr att omedelbart se frÄn applikationen vad den gör. Till exempel Àr grÀnssnittet det hÀr, det hÀr, det hÀr och det hÀr en gÄng i timmen (tidslinjen hjÀlper). Och frÄgan uppstÄr omedelbart: det verkar som att det inte Àr frontends uppgift att göra nÄgot en gÄng i timmen...

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Efter en tid insÄg vi att vi saknade aggregerade statistik efter plannoder. Vi isolerade frÄn planerna endast de noder som gör nÄgot med sjÀlva tabellernas data (lÀs/skriv dem efter index eller inte). Faktum Àr att bara en aspekt lÀggs till i förhÄllande till föregÄende bild - hur mÄnga poster gav den hÀr noden oss?, och hur mÄnga som kasserades (rader togs bort med filter).

Du har inte ett lĂ€mpligt index pĂ„ plattan, du gör en förfrĂ„gan till det, det flyger förbi indexet, hamnar i Seq Scan... du har filtrerat bort alla poster utom en. Varför behöver du 100 miljoner filtrerade poster per dag? Är det inte bĂ€ttre att rulla upp indexet?

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

Efter att ha analyserat alla planerna nod för nod insÄg vi att det finns nÄgra typiska strukturer i planerna som med stor sannolikhet kommer att se misstÀnkta ut. Och det skulle vara trevligt att berÀtta för utvecklaren: "VÀn, hÀr lÀser du först efter index, sorterar sedan och klipper sedan av" - som regel finns det en post.

Alla som skrev frÄgor har förmodligen stött pÄ det hÀr mönstret: "Ge mig den sista bestÀllningen för Vasya, dess datum." Och om du inte har ett index efter datum, eller om det inte finns nÄgot datum i indexet du anvÀnde, kommer du att trampa pÄ exakt samma "rake" .

Men vi vet att detta Àr en "rake" - sÄ varför inte omedelbart berÀtta för utvecklaren vad han ska göra. Följaktligen, nÀr vi öppnar en plan nu, ser vÄr utvecklare omedelbart en vacker bild med tips, dÀr de omedelbart sÀger till honom: "Du har problem hÀr och dÀr, men de Àr lösta pÄ det hÀr sÀttet och det sÀttet."

Som ett resultat har mÀngden erfarenhet som behövdes för att lösa problem i början och nu minskat avsevÀrt. Det Àr den hÀr typen av verktyg vi har.

Massoptimering av PostgreSQL-frÄgor. Kirill Borovikov (Tensor)

KĂ€lla: will.com

LĂ€gg en kommentar