Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Správa uvádza niektoré prístupy, ktoré to umožňujú monitorovať výkon SQL dotazov, keď sú ich za deň miliónya existujú stovky monitorovaných PostgreSQL serverov.

Aké technické riešenia nám umožňujú efektívne spracovať taký objem informácií a ako to uľahčuje život bežnému vývojárovi?


koho to zaujíma? analýzy špecifických problémov a rôznych optimalizačných techník SQL dotazy a riešenie typických problémov DBA v PostgreSQL - môžete tiež prečítajte si sériu článkov na túto tému.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)
Volám sa Kirill Borovikov, zastupujem Tenzorová spoločnosť. Konkrétne sa v našej spoločnosti špecializujem na prácu s databázami.

Dnes vám poviem, ako optimalizujeme dopyty, keď nepotrebujete „rozoberať“ výkon jedného dopytu, ale problém vyriešiť hromadne. Keď existujú milióny žiadostí a musíte nejaké nájsť prístupy k riešeniu tento veľký problém.

Vo všeobecnosti je Tensor pre milión našich klientov VLSI je naša aplikácia: firemná sociálna sieť, riešenia pre videokomunikáciu, pre interný a externý tok dokladov, účtovné systémy pre účtovníctvo a sklady,... Teda taký “megakombinát” pre integrované riadenie podniku, v ktorom je viac ako 100 rôznych interné projekty.

Aby sme zaistili, že všetky fungujú a rozvíjajú sa normálne, máme 10 vývojových centier po celej krajine, v ktorých je viac 1000 vývojárov.

S PostgreSQL pracujeme od roku 2008 a nahromadili sme veľké množstvo toho, čo spracovávame - klientske dáta, štatistické, analytické, dáta z externých informačných systémov - viac ako 400 TB. Len vo výrobe je okolo 250 serverov a celkovo je to okolo 1000 databázových serverov, ktoré monitorujeme.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

SQL je deklaratívny jazyk. Opisujete nie „ako“ by niečo malo fungovať, ale „čo“ chcete dosiahnuť. DBMS vie lepšie, ako urobiť JOIN - ako prepojiť vaše tabuľky, aké podmienky uložiť, čo prejde indexom, čo nie...

Niektoré DBMS prijímajú rady: „Nie, spojte tieto dve tabuľky do takej a takej fronty“, ale PostgreSQL to nedokáže. Toto je vedomá pozícia popredných vývojárov: „Radšej dokončíme optimalizátor dotazov, než aby sme vývojárom umožnili používať nejaké rady.“

Ale napriek tomu, že PostgreSQL neumožňuje „vonku“ ovládať sa, dokonale to umožňuje vidieť, čo sa v ňom dejekeď spustíte dopyt a kde má problémy.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Vo všeobecnosti, s akými klasickými problémami vývojár [do DBA] zvyčajne prichádza? „Tu sme splnili požiadavku, a u nás ide všetko pomaly, všetko visí, niečo sa deje... Nejaký problém!“

Dôvody sú takmer vždy rovnaké:

  • neefektívny dotazovací algoritmus
    Vývojár: „Teraz mu dávam 10 tabuliek v SQL cez JOIN...“ – a očakáva, že jeho podmienky sa zázračne efektívne „rozviažu“ a všetko rýchlo získa. Zázraky sa ale nedejú a každý systém s takou variabilitou (10 tabuliek v jednej FROM) vždy dáva nejakú chybu. [článok]
  • zastarané štatistiky
    Tento bod je veľmi dôležitý najmä pre PostgreSQL, keď na server „nalejete“ veľkú množinu údajov, zadáte požiadavku a tá „sexcanit“ váš tablet. Pretože včera v ňom bolo 10 záznamov a dnes je ich 10 miliónov, ale PostgreSQL si to ešte neuvedomuje a musíme mu o tom povedať. [článok]
  • „zapojte“ zdroje
    Nainštalovali ste veľkú a silne zaťaženú databázu na slabý server, ktorý nemá dostatočný výkon disku, pamäte alebo procesora. A to je všetko... Niekde je výkonnostný strop, nad ktorým už nemôžete skákať.
  • blokovanie
    Toto je zložitý bod, ale najviac sa týkajú rôznych modifikačných dotazov (INSERT, UPDATE, DELETE) – toto je samostatná veľká téma.

Získanie plánu

...A na všetko ostatné my potrebovať plán! Musíme vidieť, čo sa deje na serveri.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Plán vykonávania dotazov pre PostgreSQL je strom algoritmu vykonávania dotazu v textovej reprezentácii. Je to práve algoritmus, ktorý sa na základe analýzy plánovača ukázal ako najefektívnejší.

Každý uzol stromu je operácia: získavanie údajov z tabuľky alebo indexu, vytváranie bitmapy, spájanie dvoch tabuliek, spájanie, pretínanie alebo vylúčenie výberov. Vykonanie dotazu zahŕňa prechádzanie uzlami tohto stromu.

Ak chcete získať plán dotazov, najjednoduchším spôsobom je vykonať príkaz EXPLAIN. Ak chcete získať všetky skutočné atribúty, to znamená skutočne vykonať dotaz na základni - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Zlá časť: keď ho spustíte, deje sa to „tu a teraz“, takže je vhodný len na lokálne ladenie. Ak vezmete vysoko zaťažený server, ktorý je pod silným tokom zmien údajov, a uvidíte: „Ach! Tu máme pomalú realizáciuxia žiadosť." Pred pol hodinou, hodinou - kým ste bežali a získavali túto požiadavku z protokolov a privádzali ju späť na server, zmenila sa celá vaša množina údajov a štatistiky. Spustíte ho na ladenie - a beží rýchlo! A ty nemôžeš pochopiť prečo, prečo to bolo pomaly.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Aby pochopili, čo sa stalo presne v momente, keď bola požiadavka vykonaná na serveri, napísali inteligentní ľudia modul auto_explain. Je prítomný takmer vo všetkých najbežnejších distribúciách PostgreSQL a možno ho jednoducho aktivovať v konfiguračnom súbore.

Ak si uvedomí, že nejaká požiadavka beží dlhšie ako limit, ktorý ste jej prikázali, urobí to „snímku“ plánu tejto požiadavky a zapíše ich spolu do denníka.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Teraz sa zdá byť všetko v poriadku, ideme do logu a uvidíme tam... [textová obuv]. Ale nemôžeme o tom povedať nič, okrem skutočnosti, že je to vynikajúci plán, pretože jeho vykonanie trvalo 11 ms.

Všetko sa zdá byť v poriadku – nič však nie je jasné, čo sa vlastne stalo. Okrem všeobecného času v skutočnosti nič nevidíme. Pretože pohľad na takého „jahňaťa“ obyčajného textu vo všeobecnosti nie je vizuálny.

Ale aj keď to nie je zrejmé, aj keď je to nepohodlné, existujú zásadnejšie problémy:

  • Uzol označuje súčet zdrojov celého podstromu pod ním. To znamená, že nemôžete len zistiť, koľko času ste strávili týmto konkrétnym indexovým skenovaním, ak je pod ním nejaký vnorený stav. Musíme sa dynamicky pozrieť, či sú vo vnútri „deti“ a podmienené premenné, CTE – a toto všetko „v našich mysliach“ odpočítať.
  • Druhý bod: čas, ktorý je uvedený na uzle je čas vykonania jedného uzla. Ak bol tento uzol vykonaný v dôsledku napríklad niekoľkonásobného prejdenia záznamov tabuľky, potom sa počet slučiek – cyklov tohto uzla – v pláne zvýši. Ale samotný čas vykonania atómu zostáva z hľadiska plánu rovnaký. To znamená, že aby ste pochopili, ako dlho bol tento uzol celkovo vykonaný, musíte vynásobiť jednu vec druhou - opäť „vo vašej hlave“.

V takýchto situáciách pochopte „Kto je najslabší článok? takmer nemožné. Preto aj samotní vývojári píšu do „manuálu“, že „Pochopiť plán je umenie, ktoré sa treba naučiť, zažiť...“.

Máme však 1000 XNUMX vývojárov a túto skúsenosť nemôžete sprostredkovať každému z nich. Ja, ty, on to viem, ale niekto tam to už nevie. Možno sa naučí, možno nie, ale teraz potrebuje pracovať – a kde by získal túto skúsenosť?

Vizualizácia plánu

Preto sme si uvedomili, že na to, aby sme sa s týmito problémami vysporiadali, potrebujeme dobrá vizualizácia plánu. [článok]

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Najprv sme prešli „trhom“ – pozrime sa na internet, aby sme videli, čo vôbec existuje.

Ukázalo sa však, že existuje len veľmi málo relatívne „živých“ riešení, ktoré sa viac-menej vyvíjajú – doslova len jedno: vysvetli.depesz.com od Huberta Lubaczewského. Keď do poľa „feed“ zadáte textovú reprezentáciu plánu, zobrazí sa vám tabuľka s analyzovanými údajmi:

  • vlastný čas spracovania uzla
  • celkový čas pre celý podstrom
  • počet záznamov, ktoré boli nájdené a ktoré boli štatisticky očakávané
  • samotné telo uzla

Táto služba má tiež možnosť zdieľať archív odkazov. Hodili ste tam svoj plán a povedali ste: "Hej, Vasya, tu je odkaz, niečo tam nie je v poriadku."

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Existujú však aj malé problémy.

Po prvé, obrovské množstvo „copy-paste“. Vezmeš kus polena, zapichneš ho tam a znova a znova.

Po druhé, žiadna analýza množstva prečítaných údajov — rovnaké vyrovnávacie pamäte ako výstup EXPLAIN (ANALYZE, BUFFERS), tu to nevidíme. Jednoducho ich nevie rozobrať, pochopiť a pracovať s nimi. Keď čítate veľa údajov a uvedomíte si, že môžete nesprávne prideliť diskovú a pamäťovú vyrovnávaciu pamäť, táto informácia je veľmi dôležitá.

Tretím negatívnym bodom je veľmi slabý vývoj tohto projektu. Potvrdenia sú veľmi malé, je dobré, ak raz za šesť mesiacov a kód je v jazyku Perl.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Ale toto sú všetko „texty“, s tým by sme mohli nejako žiť, ale je tu jedna vec, ktorá nás od tejto služby veľmi odvrátila. Ide o chyby v analýze Common Table Expression (CTE) a rôznych dynamických uzlov ako InitPlan/SubPlan.

Ak veríte tomuto obrázku, potom je celkový čas vykonania každého jednotlivého uzla väčší ako celkový čas vykonania celej požiadavky. Je to jednoduché - čas generovania tohto CTE nebol odpočítaný od uzla CTE Scan. Preto už nepoznáme správnu odpoveď na to, ako dlho trvalo samotné skenovanie CTE.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Potom sme si uvedomili, že je čas napísať si vlastné - hurá! Každý vývojár hovorí: „Teraz napíšeme svoje vlastné, bude to super jednoduché!“

Vzali sme zásobník typický pre webové služby: jadro založené na Node.js + Express, použili sme Bootstrap a D3.js na krásne diagramy. A naše očakávania boli plne oprávnené - prvý prototyp sme dostali za 2 týždne:

  • vlastný analyzátor plánov
    To znamená, že teraz môžeme analyzovať akýkoľvek plán z plánov generovaných PostgreSQL.
  • správna analýza dynamických uzlov - CTE Scan, InitPlan, SubPlan
  • analýza distribúcie pufrov - kde sa načítavajú dátové stránky z pamäte, kde z lokálnej vyrovnávacej pamäte, kde z disku
  • dostal jasnosť
    Aby ste to všetko „neprehrabovali“ v denníku, ale aby ste na obrázku hneď videli „najslabší článok“.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Máme niečo také, vrátane zvýraznenia syntaxe. Ale väčšinou už naši vývojári nepracujú s kompletným znázornením plánu, ale s kratším. Veď už sme všetky čísla rozobrali a hodili doľava a doprava a v strede sme nechali len prvý riadok, aký je to uzol: CTE Scan, CTE generation alebo Seq Scan podľa nejakého znaku.

Toto je skrátená reprezentácia, ktorú nazývame šablóna plánu.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Čo by sa ešte hodilo? Bolo by vhodné vidieť, aký podiel z nášho celkového času je pridelený ktorému uzlu - a jednoducho ho „prilepiť“ na stranu koláčový graf.

Ukazujeme na uzol a vidíme - ukázalo sa, že Seq Scan zabral menej ako štvrtinu celkového času a zvyšné 3/4 zabral CTE Scan. Hrôza! Toto je malá poznámka o „rýchlosti streľby“ CTE Scan, ak ich aktívne používate vo svojich dopytoch. Nie sú veľmi rýchle - sú horšie ako bežné skenovanie stola. [článok] [článok]

Ale zvyčajne sú takéto diagramy zaujímavejšie, zložitejšie, keď okamžite ukážeme na segment a vidíme, že napríklad viac ako polovicu času nejaký Seq Scan „zjedol“. Navyše vo vnútri bol akýsi Filter, podľa neho bolo vyradených veľa záznamov... Tento obrázok môžete priamo hodiť vývojárovi a povedať: „Vasya, všetko je tu pre teba zlé! Zisti to, pozri - niečo nie je v poriadku!"

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Prirodzene, boli tam aj nejaké „hrable“.

Prvá vec, na ktorú sme narazili, bol problém so zaokrúhľovaním. Čas každého jednotlivého uzla v pláne je uvedený s presnosťou 1 μs. A keď počet cyklov uzla presiahne napríklad 1000 - po vykonaní PostgreSQL rozdelený „s presnosťou“, potom pri spätnom výpočte dostaneme celkový čas „niekde medzi 0.95 ms a 1.05 ms“. Keď počet prejde na mikrosekundy, je to v poriadku, ale keď už sú to [mili]sekundy, musíte túto informáciu vziať do úvahy pri „rozväzovaní“ zdrojov na uzly plánu „kto koľko spotreboval“.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Druhý bod, zložitejší, je rozdelenie zdrojov (tých vyrovnávacích pamätí) medzi dynamické uzly. To nás stálo prvé 2 týždne prototypu plus ďalšie 4 týždne.

Je celkom ľahké získať tento druh problému - robíme CTE a údajne si v ňom niečo prečítame. V skutočnosti je PostgreSQL „inteligentný“ a priamo tam nič neprečíta. Potom z toho vezmeme prvý záznam a k nemu sto prvý z toho istého CTE.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Pozeráme sa na plán a chápeme - je to zvláštne, máme 3 vyrovnávacie pamäte (údajové stránky) „spotrebované“ v Seq Scan, 1 viac v CTE Scan a 2 ďalšie v druhom CTE Scan. To znamená, že ak všetko jednoducho zhrnieme, dostaneme 6, ale z tabletu prečítame iba 3! CTE Scan nečíta nič odkiaľkoľvek, ale pracuje priamo s pamäťou procesu. To znamená, že tu niečo nie je v poriadku!

V skutočnosti sa ukázalo, že tu sú všetky tie 3 strany údajov, ktoré boli vyžiadané od Seq Scan, najprv 1 požiadala o 1. CTE sken a potom mu bola prečítaná 2. a ďalšie 2. To znamená, že spolu 3 strany boli prečítané údaje, nie 6.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

A tento obrázok nás priviedol k pochopeniu, že realizácia plánu už nie je strom, ale jednoducho nejaký acyklický graf. A dostali sme takýto diagram, aby sme pochopili, „odkiaľ pochádzalo“. To znamená, že tu sme vytvorili CTE z pg_class a požiadali sme o to dvakrát, a takmer všetok náš čas sme strávili na pobočke, keď sme o to požiadali druhýkrát. Je jasné, že prečítanie 2. záznamu je oveľa drahšie ako len prečítanie 101. záznamu z tabletu.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Chvíľu sme si vydýchli. Povedali: „Teraz, Neo, poznáš kung-fu! Teraz je naša skúsenosť priamo na vašej obrazovke. Teraz to môžeš použiť." [článok]

Konsolidácia guľatiny

Našich 1000 vývojárov si vydýchlo. Pochopili sme však, že máme len stovky „bojových“ serverov a všetko toto „kopírovanie a vkladanie“ zo strany vývojárov nie je vôbec pohodlné. Uvedomili sme si, že to musíme zbierať sami.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Vo všeobecnosti existuje štandardný modul, ktorý dokáže zhromažďovať štatistiky, je však potrebné ho aktivovať aj v konfigurácii - toto modul pg_stat_statements. Ten nám však nevyhovoval.

Po prvé, priraďuje k rovnakým dopytom pomocou rôznych schém v rámci tej istej databázy rôzne QueryId. To znamená, že ak to najprv urobíte SET search_path = '01'; SELECT * FROM user LIMIT 1;a potom SET search_path = '02'; a rovnakú požiadavku, potom budú mať štatistiky tohto modulu rôzne záznamy a nebudem môcť zbierať všeobecné štatistiky konkrétne v kontexte tohto profilu žiadosti bez zohľadnenia schém.

Druhý bod, ktorý nám bránil v jeho používaní, je nedostatok plánov. To znamená, že neexistuje žiadny plán, existuje len samotná žiadosť. Vidíme, čo sa spomalilo, ale nechápeme prečo. A tu sa vraciame k problému rýchlo sa meniaceho súboru údajov.

A posledná chvíľa - nedostatok "faktov". To znamená, že nemôžete riešiť konkrétnu inštanciu vykonania dotazu – neexistuje žiadna, existujú iba agregované štatistiky. Aj keď sa s tým dá pracovať, je to len veľmi ťažké.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Preto sme sa rozhodli bojovať proti copy-paste a začali sme písať zberateľ.

Kolektor sa pripojí cez SSH, vytvorí zabezpečené pripojenie k serveru s databázou pomocou certifikátu a tail -F „priľne“ k nemu v protokolovom súbore. Takže v tejto relácii získame úplné „zrkadlenie“ celého súboru denníka, ktoré server vygeneruje. Zaťaženie samotného servera je minimálne, pretože tam nič neanalyzujeme, iba zrkadlíme návštevnosť.

Keďže sme už začali písať rozhranie v Node.js, pokračovali sme v písaní kolektora v ňom. A táto technológia sa ospravedlnila, pretože je veľmi výhodné používať JavaScript na prácu so slabo formátovanými textovými údajmi, ktorými sú denník. A samotná infraštruktúra Node.js ako backendová platforma vám umožňuje jednoducho a pohodlne pracovať so sieťovými pripojeniami a vlastne s akýmikoľvek dátovými tokmi.

V súlade s tým „roztiahneme“ dve spojenia: prvé, aby sme „počúvali“ samotný denník a vzali si ho k sebe, a druhé, aby sme sa pravidelne pýtali na základňu. "Ale protokol ukazuje, že znak s oid 123 je zablokovaný," ale pre vývojára to nič neznamená a bolo by pekné opýtať sa databázy: "Čo je vlastne OID = 123?" A tak sa pravidelne pýtame základne, čo o sebe ešte nevieme.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

"Je tu len jedna vec, ktorú ste nebrali do úvahy, existuje druh včiel podobných slonom!" Tento systém sme začali vyvíjať, keď sme chceli monitorovať 10 serverov. Najkritickejšie v našom chápaní, kde sa vyskytli problémy, s ktorými bolo ťažké sa vysporiadať. Ale počas prvého kvartálu sme dostali stovku za monitoring – pretože systém fungoval, každý ho chcel, každý bol pohodlný.

Toto všetko treba zrátať, dátový tok je veľký a aktívny. V skutočnosti to, čo sledujeme, s čím si vieme poradiť, to používame. PostgreSQL používame aj ako dátové úložisko. A nič nie je rýchlejšie, ako do neho „naliať“ dáta, ako operátor COPY Ešte nie.

Ale jednoducho „nalievanie“ údajov nie je v skutočnosti našou technológiou. Pretože ak máte približne 50 100 požiadaviek za sekundu na stovke serverov, vygeneruje to 150 – XNUMX GB denníkov za deň. Preto sme museli základňu opatrne „vyrezať“.

Po prvé, urobili sme rozdelenie podľa dňa, pretože vo všeobecnosti nikoho nezaujíma korelácia medzi dňami. Aký je rozdiel v tom, čo ste mali včera, ak ste dnes večer spustili novú verziu aplikácie – a už nejaké nové štatistiky.

Po druhé, naučili sme sa (boli prinútení) veľmi, veľmi rýchlo písať pomocou COPY. Teda nielen COPYpretože je rýchlejší ako INSERTa ešte rýchlejšie.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Tretí bod – musel som opustiť spúšťače a cudzie kľúče. To znamená, že nemáme vôbec žiadnu referenčnú integritu. Pretože ak máte tabuľku, ktorá má pár FK, a v štruktúre databázy poviete, že „tu je záznam protokolu, na ktorý sa FK odkazuje, napríklad na skupinu záznamov“, tak keď ho vložíte, PostgreSQL nezostáva nič iné, len ako to brať a robiť to poctivo SELECT 1 FROM master_fk1_table WHERE ... s identifikátorom, ktorý sa pokúšate vložiť - len aby ste skontrolovali, či sa tam tento záznam nachádza, aby ste pri vkladaní tento cudzí kľúč „neodlomili“.

Namiesto jedného záznamu do cieľovej tabuľky a jej indexov získame ďalšiu výhodu čítania zo všetkých tabuliek, na ktoré odkazuje. To však vôbec nepotrebujeme – našou úlohou je zaznamenať čo najviac a čo najrýchlejšie s čo najmenšou záťažou. Takže FK - dole!

Ďalším bodom je agregácia a hašovanie. Spočiatku sme ich implementovali do databázy - koniec koncov, je vhodné okamžite, keď príde záznam, urobiť to v nejakom tablete "plus jedna" priamo v spúšti. No, je to pohodlné, ale to isté zlé - vložíte jeden záznam, ale ste nútení čítať a písať niečo iné z inej tabuľky. Navyše nielen čítate a píšete, ale aj to robíte zakaždým.

Teraz si predstavte, že máte tabuľku, v ktorej jednoducho spočítate počet žiadostí, ktoré prešli cez konkrétneho hostiteľa: +1, +1, +1, ..., +1. A to v zásade nepotrebujete - je to všetko možné súčet v pamäti na zberači a odoslať do databázy jedným ťahom +10.

Áno, v prípade nejakých problémov sa vaša logická integrita môže „rozpadnúť“, ale to je takmer nereálny prípad – pretože máte normálny server, má batériu v kontroléri, máte protokol transakcií, protokol na súborový systém... Vo všeobecnosti to nestojí za to. Strata produktivity, ktorú získate spustením spúšťačov/FK, nestojí za náklady, ktoré vám vzniknú.

Rovnako je to s hašovaním. Priletí k vám určitá požiadavka, z nej v databáze vypočítate určitý identifikátor, zapíšete ho do databázy a potom to všetkým poviete. Všetko je v poriadku, kým k vám v čase nahrávania nepríde druhá osoba, ktorá chce nahrať to isté – a vás zablokujú, a to už je zlé. Preto, ak môžete preniesť generovanie niektorých ID na klienta (vo vzťahu k databáze), je lepšie to urobiť.

Perfektne sa nám osvedčilo použiť MD5 z textu - požiadavka, plán, šablóna,... Vypočítame to na strane kolektora a hotové ID „nasypeme“ do databázy. Dĺžka MD5 a denné rozdelenie nám umožňujú nebáť sa možných kolízií.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Aby sme však toto všetko rýchlo nahrali, potrebovali sme upraviť samotný postup nahrávania.

Ako zvyčajne zapisujete údaje? Máme nejaký súbor údajov, rozdelíme ho do niekoľkých tabuliek a potom KOPÍRUJEME - najprv do prvej, potom do druhej, do tretej... Je to nepohodlné, pretože sa zdá, že zapisujeme jeden tok údajov v troch krokoch. postupne. Nepríjemné. Dá sa to urobiť rýchlejšie? Môcť!

Na to stačí tieto toky paralelne rozložiť. Ukazuje sa, že chyby, požiadavky, šablóny, blokovania, ... lietajú v samostatných vláknach - a to všetko píšeme paralelne. Dosť na to ponechajte kanál COPY neustále otvorený pre každú jednotlivú cieľovú tabuľku.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Teda u zberateľa vždy je tam prúd, do ktorej si môžem zapisovať údaje, ktoré potrebujem. Aby však databáza tieto údaje videla a niekto nezostal pri čakaní na zápis týchto údajov, COPY sa musí v určitých intervaloch prerušovať. Pre nás bola najefektívnejšia perióda cca 100ms - zatvoríme a hneď zase otvoríme na ten istý stôl. A ak nemáme dostatok jedného toku počas niektorých špičiek, potom robíme pooling do určitého limitu.

Navyše sme zistili, že pre takýto profil zaťaženia je akákoľvek agregácia, keď sa záznamy zbierajú v dávkach, zlá. Klasické zlo je INSERT ... VALUES a ďalších 1000 záznamov. Pretože v tom momente máte na médiu vrchol zápisu a všetci ostatní, ktorí sa snažia niečo zapísať na disk, budú čakať.

Aby ste sa zbavili takýchto anomálií, jednoducho nič nespájajte, vôbec neukladať do vyrovnávacej pamäte. A ak dôjde k ukladaniu do vyrovnávacej pamäte na disk (našťastie vám to umožňuje Stream API v Node.js) – toto pripojenie odložte. Keď dostanete udalosť, že je opäť voľná, napíšte jej z nahromadeného frontu. A kým je zaneprázdnený, vezmite si ďalší voľný z bazéna a napíšte mu.

Pred zavedením tohto prístupu k zaznamenávaniu údajov sme mali približne 4K zápisové operácie a týmto spôsobom sme znížili záťaž 4-krát. Teraz sa vďaka novým monitorovaným databázam rozrástli o ďalších 6-krát – až 100 MB/s. A teraz ukladáme protokoly za posledné 3 mesiace v objeme približne 10 – 15 TB v nádeji, že za tri mesiace bude ktorýkoľvek vývojár schopný vyriešiť akýkoľvek problém.

Rozumieme problémom

Ale jednoduché zhromažďovanie všetkých týchto údajov je dobré, užitočné, relevantné, ale nestačí – treba to pochopiť. Pretože ide o milióny rôznych plánov denne.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Ale milióny sú nezvládnuteľné, najprv musíme urobiť „menší“. A v prvom rade sa musíte rozhodnúť, ako túto „menšiu“ vec zorganizujete.

Identifikovali sme tri kľúčové body:

  • kto odoslal túto žiadosť
    To znamená, z akej aplikácie to „prišlo“: webové rozhranie, backend, platobný systém alebo niečo iné.
  • kde stalo sa
    Na akom konkretnom serveri? Pretože ak máte pod jednou aplikáciou viacero serverov a jeden zrazu „zblbne“ (pretože „disk je zhnitý“, „vytekla pamäť“, nejaký iný problém), potom musíte konkrétne osloviť server.
  • ako problém sa prejavil tak či onak

Aby sme pochopili, „kto“ nám poslal požiadavku, používame štandardný nástroj – nastavenie premennej relácie: SET application_name = '{bl-host}:{bl-method}'; — pošleme názov hostiteľa obchodnej logiky, z ktorého požiadavka prichádza, a názov metódy alebo aplikácie, ktorá ju iniciovala.

Potom, čo sme odovzdali „vlastníkovi“ požiadavky, musí byť odoslaná do protokolu - na tento účel nakonfigurujeme premennú log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Pre záujemcov možno pozri v návodečo to všetko znamená. Ukazuje sa, že v protokole vidíme:

  • čas
  • identifikátory procesov a transakcií
  • názov databázy
  • IP osoby, ktorá odoslala túto žiadosť
  • a názov metódy

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Potom sme si uvedomili, že nie je veľmi zaujímavé pozerať sa na koreláciu jednej požiadavky medzi rôznymi servermi. Nestáva sa často, že by sa vám jedna aplikácia tu a tam pokazila rovnako. Ale aj keď je to to isté, pozrite sa na ktorýkoľvek z týchto serverov.

Takže tu je strih "jeden server - jeden deň" ukázalo sa, že nám to stačí na akýkoľvek rozbor.

Prvá analytická časť je rovnaká "vzorka" - skrátená forma prezentácie plánu, zbavená všetkých číselných ukazovateľov. Druhý rez je aplikácia alebo metóda a tretí rez je konkrétny uzol plánu, ktorý nám spôsobil problémy.

Keď sme prešli z konkrétnych inštancií na šablóny, získali sme dve výhody naraz:

  • viacnásobné zníženie počtu objektov na analýzu
    Problém už musíme analyzovať nie tisíckami dopytov či plánov, ale desiatkami šablón.
  • časovej osi
    To znamená, že zhrnutím „faktov“ v určitej časti môžete zobraziť ich vzhľad počas dňa. A tu môžete pochopiť, že ak máte nejaký vzorec, ktorý sa stane napríklad raz za hodinu, ale malo by sa to stať raz za deň, mali by ste premýšľať o tom, čo sa pokazilo - kto to spôsobil a prečo, možno by to malo byť tu nemal by. Toto je ďalšia nenumerická, čisto vizuálna metóda analýzy.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Zostávajúce metódy sú založené na ukazovateľoch, ktoré vytiahneme z plánu: koľkokrát sa takýto vzor vyskytol, celkový a priemerný čas, koľko dát bolo načítaných z disku a koľko z pamäte...

Pretože sa napríklad dostanete na analytickú stránku pre hostiteľa, pozrite sa - niečo sa na disku začína čítať príliš veľa. Disk na serveri to nezvláda - kto z neho číta?

A môžete si zoradiť podľa ľubovoľného stĺpca a rozhodnúť sa, čím sa budete práve zaoberať - záťažou procesora alebo disku, alebo celkovým počtom požiadaviek... Zoradili sme to, pozreli sme sa na tie “top”, opravili a spustila novú verziu aplikácie.
[video prednáška]

A okamžite môžete vidieť rôzne aplikácie, ktoré prichádzajú s rovnakou šablónou zo žiadosti ako SELECT * FROM users WHERE login = 'Vasya'. Frontend, backend, spracovanie... A vy sa čudujete, prečo by spracovanie čítalo používateľa, ak s ním nekomunikuje.

Opačný spôsob je okamžite vidieť z aplikácie, čo robí. Frontend je napríklad toto, toto, toto a toto raz za hodinu (pomáha časová os). A hneď vyvstáva otázka: zdá sa, že nie je úlohou frontendu robiť niečo raz za hodinu...

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Po nejakom čase sme si uvedomili, že nám chýba agregácia štatistiky podľa uzlov plánu. Z plánov sme izolovali len tie uzly, ktoré niečo robia s údajmi samotných tabuliek (čítajú/zapisujú ich podľa indexu alebo nie). V skutočnosti je k predchádzajúcemu obrázku pridaný iba jeden aspekt - koľko záznamov nám priniesol tento uzol?a koľko bolo vyradených (riadky odstránené filtrom).

Nemáte na štítku vhodný index, zadáte mu požiadavku, preletí popri indexe, spadne do Seq Scan... odfiltrovali ste všetky záznamy okrem jedného. Prečo potrebujete 100 miliónov filtrovaných záznamov za deň? Nie je lepšie zhrnúť index?

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Po analýze všetkých plánov uzol po uzle sme si uvedomili, že v plánoch sú niektoré typické štruktúry, ktoré budú veľmi pravdepodobne vyzerať podozrivo. A bolo by pekné povedať vývojárovi: „Priateľ, tu najskôr čítate podľa indexu, potom triedite a potom odrežte“ - spravidla existuje jeden záznam.

Každý, kto písal otázky, sa pravdepodobne stretol s týmto vzorom: „Dajte mi poslednú objednávku na Vasyu, jej dátum.“ A ak nemáte index podľa dátumu alebo v indexe, ktorý ste použili, nie je žiadny dátum, potom šliapnite na presne tie isté „hrable“ .

Vieme však, že ide o „hrabanie“ – tak prečo vývojárovi hneď nepoviete, čo má robiť. Preto pri otvorení plánu náš vývojár okamžite uvidí krásny obrázok s tipmi, kde mu okamžite povedia: „Máte problémy tu a tam, ale sú vyriešené tak a tak.“

V dôsledku toho množstvo skúseností, ktoré bolo potrebné na riešenie problémov na začiatku a teraz, výrazne kleslo. Toto je druh nástroja, ktorý máme.

Hromadná optimalizácia PostgreSQL dotazov. Kirill Borovikov (Tensor)

Zdroj: hab.com

Pridať komentár