Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Zpráva uvádí některé přístupy, které to umožňují sledovat výkon SQL dotazů, když jich jsou miliony denněa existují stovky kontrolovaných serverů PostgreSQL.

Jaká technická řešení nám umožňují efektivně zpracovat takový objem informací a jak to usnadňuje život běžnému vývojáři.


Koho to zajímá analýza konkrétních problémů a různé optimalizační techniky SQL dotazy a řešení typických úloh DBA v PostgreSQL - můžete také viz série článků na toto téma.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)
Jmenuji se Kirill Borovikov, zastupuji "Tensor" společnost. Konkrétně se v naší společnosti specializuji na práci s databázemi.

Dnes vám řeknu, jak se zabýváme optimalizací dotazů, kdy nepotřebujete „vychytávat“ výkon jednoho dotazu, ale řešit problém hromadně. Když existují miliony žádostí a potřebujete nějaké najít přístupy k řešení tento velký problém.

Obecně platí, že "Tensor" pro milion našich zákazníků je VLSI - naše aplikace: firemní sociální síť, řešení pro video komunikaci, pro interní i externí správu dokumentů, účetní systémy pro účetnictví a sklad, ... Tedy taková „megakombinace“ pro integrované řízení podniku, ve které je více než 100 různých interních projektů .

Aby všichni normálně fungovali a vyvíjeli se, máme 10 vývojových center po celé republice, oni mají víc 1000 vývojářů.

S PostgreSQL pracujeme od roku 2008 a nashromáždili jsme velké množství toho, co zpracováváme - jsou to klientská data, statistická, analytická, data z externích informačních systémů - přes 400 TB. Jen "ve výrobě" je asi 250 serverů a celkem je to asi 1000 databázových serverů, které monitorujeme.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

SQL je deklarativní jazyk. Nepopisujete „jak“ by něco mělo fungovat, ale „co“ chcete získat. DBMS ví lépe, jak provést JOIN - jak propojit vaše tabulky, jaké podmínky uložit, co projde indexem, co nebude ...

Některé DBMS přijímají rady: „Ne, spojte tyto dvě tabulky do takové a takové fronty“, ale PostgreSQL neví jak. Toto je vědomý postoj předních vývojářů: „Je lepší dokončit optimalizátor dotazů, než dovolit vývojářům používat nějaké rady.“

Ale, přestože PostgreSQL neumožňuje „venku“, aby se samo řídilo, dokonale to umožňuje podívejte se, co se děje uvnitřkdyž spustíte dotaz a kde má problémy.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Obecně, s jakými klasickými problémy vývojář [do DBA] obvykle přichází? „Tady jsme splnili požadavek a jsme pomalí, všechno visí, něco se děje ... Nějaký průšvih!

Důvody jsou téměř vždy stejné:

  • neefektivní dotazovací algoritmus
    Vývojář: „Nyní pro něj mám 10 tabulek v SQL přes JOIN ...“ - a očekává, že se jeho podmínky zázračně účinně „rozvážou“ a vše rychle získá. Zázraky se ale nedějí a každý systém s takovou variabilitou (10 tabulek v jedné FROM) vždy dává nějakou tu chybu. [článek]
  • zastaralé statistiky
    Konkrétně pro PostgreSQL je velmi relevantní okamžik, kdy na server „nalijete“ velký soubor dat, zadáte požadavek – a ten vás „skenuje“ na talíři. Protože včera obsahoval 10 záznamů a dnes je jich 10 milionů, ale PostgreSQL si toho ještě není vědom a je třeba ho na to upozornit. [článek]
  • „zapojování“ zdrojů
    Umístíte velkou a silně zatíženou databázi na slabý server, který nemá dostatek disku, paměti nebo výkonu samotného procesoru. A to je vše... Někde je výkonnostní strop, nad kterým už nelze skákat.
  • blokování
    Těžká chvíle, ale nejvíce se týkají různých modifikačních dotazů (INSERT, UPDATE, DELETE) - to je samostatné velké téma.

Získejte plán

… A pro všechno ostatní my potřebovat plán! Musíme vidět, co se děje uvnitř serveru.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Plán provádění dotazů pro PostgreSQL je strom algoritmů provádění dotazů v textové reprezentaci. Přesně ten algoritmus, který byl na základě analýzy plánovače uznán jako nejúčinnější.

Každý uzel stromu je operace: extrahování dat z tabulky nebo indexu, sestavení bitmapy, spojení dvou tabulek, spojení, protínání nebo vyloučení výběrů. Provedení dotazu je procházkou po uzlech tohoto stromu.

Chcete-li získat plán dotazů, nejjednodušším způsobem je provést příkaz EXPLAIN. Chcete-li získat všechny skutečné atributy, tj. skutečně provést dotaz na základně − EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Špatný bod: když jej spustíte, stane se to "tady a teď", takže je vhodný pouze pro místní ladění. Pokud vezmete nějaký vysoce zatížený server, který je pod silným proudem změn dat, a uvidíte: „Hej! Zde jsme pomalu vystupovalisya žádost." Před půl hodinou, hodinou – zatímco jste běželi a získávali tento požadavek z protokolů a přenášeli jej zpět na server, změnila se celá vaše datová sada a statistiky. Spustíte jej k ladění – a běží rychle! A nemůžete pochopit "proč", proč byl pomalu.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Chytří lidé napsali, aby pochopili, co přesně bylo v okamžiku, kdy je požadavek na serveru vykonán modul auto_explain. Je přítomen téměř ve všech nejběžnějších distribucích PostgreSQL a lze jej jednoduše aktivovat v konfiguračním souboru.

Pokud pochopí, že nějaká žádost běží déle, než je limit, který jste mu řekli, udělá to "snímek" plán této žádosti a zapíše je společně do logu.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Nyní se zdá být vše v pořádku, jdeme do klády a vidíme tam ... [textilní plátno]. Ale nemůžeme o tom nic říct, kromě skutečnosti, že je to skvělý plán, protože jeho dokončení trvalo 11 ms.

Vše se zdá být v pořádku – ale nic není jasné, co se vlastně stalo. Kromě obecného času nevidíme nic zvláštního. Protože pohled na takový „spratek“ prostý text je obecně rozkošný.

Ale i když to není zřejmé, i když je to nepohodlné, existují mnohem zásadnější problémy:

  • Uzel označuje součet přes zdroje celého podstromu pod ním. To znamená, že je prostě nemožné zjistit, kolik času zde konkrétně strávil tento Index Scan - je nemožné, pokud je pod ním nějaká vnořená podmínka. Musíme dynamicky vidět, zda jsou uvnitř „děti“ a podmíněné proměnné, CTE – a toto vše „v mysli“ odečíst.
  • Druhý bod: čas, který je uveden na uzlu, je doba provádění uzlu. Pokud byl tento uzel proveden vícekrát v důsledku např. cyklu tabulkových záznamů, pak plán zvyšuje počet smyček - cyklů tohoto uzlu. Ale samotná doba provádění atomu zůstává v plánu stejná. To znamená, že abychom pochopili, jak moc byl tento uzel celkově proveden, musíme jeden vynásobit - opět „v mysli“.

V takových scénářích pochopte „Kdo je nejslabším článkem?“ prakticky nereálné. Proto to píší i samotní vývojáři v "manuálu". "Porozumět plánu je umění, které je třeba se naučit, zkušenost...".

Ale máme 1000 vývojářů a tyto zkušenosti nelze předat každému z nich. Já, ty, on - oni vědí, ale někdo támhle - už ne. Možná se to naučí, možná ne, ale teď potřebuje pracovat - a kde by tuto zkušenost získal.

Vizualizace plánu

Proto jsme si uvědomili, že abychom se s těmito problémy vypořádali, potřebujeme dobrá vizualizace plánu. [článek]

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Nejprve jsme šli „na trh“ – podívejme se na internet, co obecně existuje.

Ukázalo se však, že existuje jen velmi málo relativně „živých“ řešení, která jsou více či méně rozvinutá – doslova jedna věc: vysvětlit.depesz.com od Huberta Lubaczewského. Na vstupu do pole „krmit“ textové znázornění plánu vám ukáže štítek s analyzovanými daty:

  • čas zpracování vlastního uzlu
  • celkový čas v celém podstromu
  • počet záznamů, které byly získány a které byly statisticky očekávány
  • samotné tělo uzlu

Tato služba má také možnost sdílet archiv odkazů. Hodil jsi tam svůj plán a řekl jsi: "Hej, Vasyo, tady je pro tebe odkaz, něco je tam špatně."

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Jsou tu ale i drobné problémy.

Za prvé, obrovské množství "copy-paste". Vezmeš kus polena, vložíš ho tam a znovu a znovu.

Za druhé, žádná analýza množství přečtených dat - stejné vyrovnávací paměti jako výstupy EXPLAIN (ANALYZE, BUFFERS), tady to nevidíme. Jednoduše je neumí rozebrat, pochopit a pracovat s nimi. Když čtete velké množství dat a uvědomujete si, že se nemusí správně rozkládat přes disk a mezipaměť v paměti, je tato informace velmi důležitá.

Třetím negativním bodem je velmi špatný vývoj tohoto projektu. Commity jsou velmi malé, je dobré, když jednou za půl roku, a kód je v Perlu.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Ale to jsou všechno "texty", s tím by se dalo nějak žít, ale je tu jedna věc, která nás od této služby odvrátila. Jedná se o chyby analýzy Common Table Expression (CTE) a různé dynamické uzly, jako je InitPlan/SubPlan.

Pokud věříte tomuto obrázku, pak máme celkový čas provedení každého jednotlivého uzlu větší než celkový čas provedení celého požadavku. Všechno je jednoduché - čas generování tohoto CTE nebyl odečten od uzlu CTE Scan. Proto již neznáme správnou odpověď, jak dlouho samotné skenování CTE trvalo.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Pak jsme si uvědomili, že je čas napsat vlastní - hurá! Každý vývojář říká: "Nyní napíšeme vlastní, bude to super snadné!"

Vzali jsme zásobník typický pro webové služby: jádro na Node.js + Express, natažené na Bootstrap a D3.js pro krásné diagramy. A naše očekávání byla plně oprávněná - první prototyp jsme obdrželi za 2 týdny:

  • vlastní analyzátor plánů
    To znamená, že nyní můžeme obecně analyzovat jakýkoli plán z těch, které generuje PostgreSQL.
  • správná analýza dynamických uzlů - CTE Scan, InitPlan, SubPlan
  • analýza rozložení pufrů - kde se čtou datové stránky z paměti, kde z lokální cache, kde z disku
  • dostal viditelnost
    Abyste „nekopali“ vše v logu, ale viděli „nejslabší článek“ hned na obrázku.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Dostali jsme něco jako tento obrázek - okamžitě se zvýrazněním syntaxe. Ale obvykle naši vývojáři již nepracují s plnou reprezentací plánu, ale s něčím, co je kratší. Koneckonců, všechna čísla jsme už rozebrali a hodili doleva a doprava a nechali jen první řádek uprostřed, co je to za uzel: CTE Scan, CTE generace nebo Seq Scan podle nějakého štítku.

Toto je zkrácená reprezentace, kterou nazýváme šablona plánu.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Co jiného by bylo pohodlné? Bylo by vhodné vidět, jaký podíl z celkového času je distribuován do kterého uzlu - a jednoduše jsme to „přilepili“ na stranu výsečový graf.

Ukážeme na uzel a uvidíme – ukázalo se, že Seq Scan zabral méně než čtvrtinu celkového času a zbývající 3/4 zabral CTE Scan. Hrůza! Toto je malá poznámka o "rychlosti střelby" CTE Scan, pokud je aktivně používáte ve svých dotazech. Nejsou příliš rychlé - prohrávají i při běžném skenování tabulky. [článek] [článek]

Většinou jsou ale takové diagramy zajímavější, složitější, kdy hned ukážeme na segment, a vidíme, že třeba nějaký Seq Scan více než polovinu času „sežral“. Navíc uvnitř byl nějaký filtr, přes něj bylo vyřazeno mnoho záznamů... Tento obrázek můžete přímo hodit vývojáři a říct: „Vasya, tady je všechno špatně! Zjistěte to, podívejte se - něco je špatně!

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Samozřejmě, že bez "hrabání" se neobejde.

První, na co „šlápli“, byl problém se zaokrouhlováním. Čas uzlu každého jednotlivého uzlu v plánu je indikován s přesností 1 µs. A když počet cyklů uzlů překročí např. 1000 – po spuštění se PostgreSQL rozdělí „až“, tak při zpětném výpočtu dostaneme celkový čas „někde mezi 0.95 ms a 1.05 ms“. Když počet přejde na mikrosekundy, je to stále nic, ale když už je to do [mili] sekund, musíte tuto informaci vzít v úvahu při „odkroucení“ zdrojů podle uzlů plánu „kdo z koho kolik spotřeboval“.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Druhým bodem, který je složitější, je rozdělení zdrojů (těch vyrovnávacích pamětí) mezi dynamické uzly. První 2 týdny na prototyp nás to stálo další 4 týdny.

Dostat se k takovému problému je docela snadné – děláme CTE a údajně si v něm něco přečteme. Ve skutečnosti je PostgreSQL „chytrý“ a přímo tam nic nepřečte. Pak z něj vezmeme první záznam a k němu sto první ze stejného CTE.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Podíváme se na plán a pochopíme - kupodivu jsme měli 3 buffery (datové stránky) "spotřebované" v Seq Scan, 1 další v CTE Scan a 2 další v druhém CTE Scan. To znamená, že když vše jednoduše sečteme, dostaneme 6, ale z tabletu přečteme pouze 3! CTE Scan odnikud nic nečte, ale pracuje přímo s pamětí procesu. Tak tady je evidentně něco špatně!

Ve skutečnosti se ukazuje, že zde byly všechny ty 3 stránky dat, které byly požadovány od Seq Scan, nejprve 1 požádala o 1. sken CTE a poté se do něj načetla 2. a další 2. Tedy celkem 3 stránek byla přečtena data, nikoli 6.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

A tento obrázek nás přivedl k pochopení, že provedení plánu už není strom, ale jen jakýsi acyklický graf. A dostali jsme něco jako tento diagram, abychom pochopili „něco, odkud to vůbec přišlo“. To znamená, že zde jsme vytvořili CTE z pg_class a zeptali jsme se ho dvakrát a téměř celou dobu nás to vzalo podél větve, když jsme o něj požádali podruhé. Je jasné, že čtení 2. zápisu je mnohem dražší než jen 101. z tabulky.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Chvíli jsme oddychovali. Řekl: „Teď, Neo, znáš kung-fu! Nyní je naše zkušenost přímo na vaší obrazovce. Teď to můžeš použít." [článek]

Konsolidace protokolů

Našich 1000 vývojářů si oddechlo. Ale pochopili jsme, že máme jen stovky "bojových" serverů a všechno to "copy-paste" ze strany vývojářů není vůbec pohodlné. Uvědomili jsme si, že to musíme sbírat sami.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Obecně existuje běžný modul, který umí shromažďovat statistiky, ale také je třeba jej aktivovat v konfiguraci - toto modul pg_stat_statements. Ten nám ale nevyhovoval.

Za prvé, přiřadí stejné dotazy pod různými schématy v rámci stejné databáze různá QueryId. Tedy pokud to uděláte jako první SET search_path = '01'; SELECT * FROM user LIMIT 1;a pak SET search_path = '02'; a stejný dotaz, pak budou ve statistikách tohoto modulu různé záznamy a nebudu moci sbírat obecné statistiky konkrétně v kontextu tohoto profilu dotazu, aniž bych vzal v úvahu schémata.

Druhý bod, který nám bránil v jeho použití - nedostatek plánů. To znamená, že neexistuje žádný plán, existuje pouze samotná žádost. Vidíme, co se zpomalilo, ale nechápeme proč. A zde se vracíme k problému rychle se měnícího datasetu.

A poslední okamžik - nedostatek "faktů". To znamená, že nemůžete oslovit konkrétní instanci spuštění dotazu - neexistuje, existuje pouze agregovaná statistika. I když se s tím dá pracovat, je to jen velmi obtížné.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Proto jsme se rozhodli bojovat proti „copy-paste“ a začali jsme psát kolektor.

Kolektor se připojí přes SSH, „natáhne“ zabezpečené připojení k serveru s databází pomocí certifikátu a tail -F "přilne" k němu v souboru protokolu. Takže v této relaci získáme kompletní "zrcadlení" celého log souboru, který je generován serverem. Zároveň je zatížení samotného serveru minimální, protože tam nic neparsujeme, pouze zrcadlíme provoz.

Protože jsme již začali psát rozhraní v Node.js, pokračovali jsme v psaní kolektoru na něm. A tato technologie se ospravedlňovala, protože je velmi vhodné používat JavaScript pro práci s lehce formátovanými textovými daty, což je protokol. A samotná infrastruktura Node.js jako backendová platforma usnadňuje a pohodlně pracuje se síťovými připojeními a skutečně s nějakými druhy datových toků.

V souladu s tím „roztahujeme“ dvě spojení: první, abychom „poslouchali“ samotný protokol a vzali si ho k sobě, a druhé, abychom se pravidelně zeptali základny. "Ale v protokolu se objevilo, že tablet s oid 123 byl zablokován," ale to pro vývojáře nic neznamená a bylo by hezké zeptat se základny "Co je to vůbec OID = 123?" A tak se pravidelně ptáme základny na to, co ještě nevíme.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

"Jen jednu věc jste vynechali, existuje druh včel podobných slonům! .." Tento systém jsme začali vyvíjet, když jsme chtěli monitorovat 10 serverů. Nejkritičtější v našem chápání, na které se objevily některé problémy, se kterými bylo obtížné se vypořádat. Ale během prvního čtvrtletí jsme dostali stovku na monitorování - protože systém „přišel“, všichni ho chtěli, je to pohodlné pro všechny.

To vše se musí sečíst, datový tok je velký, aktivní. Vlastně to, co sledujeme, s čím si umíme poradit, to používáme. PostgreSQL také používáme jako úložiště dat. A nic není rychlejšího „nasypat“ do něj data než operátor COPY Ještě ne.

Ale pouhé „nalévání“ dat není přesně naše technologie. Protože pokud máte asi 50 100 požadavků za sekundu na stovce serverů, vygeneruje vám to 150–XNUMX GB protokolů za den. Proto jsme museli základnu pečlivě „ořezat“.

Nejprve jsme to udělali rozdělení podle dne, protože celkově nikoho nezajímá korelace mezi dny. Jaký rozdíl je v tom, co jste měli včera, pokud jste dnes večer spustili novou verzi aplikace - a již nějaké nové statistiky.

Za druhé jsme se naučili (byli nuceni) velmi rychle se s ním píše COPY. Tedy nejen COPYprotože je rychlejší než INSERTa ještě rychleji.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Třetí moment – ​​musel jsem spouštěče odmítnutí a z cizích klíčů. To znamená, že vůbec nemáme referenční integritu. Protože pokud máte tabulku, která má na sobě pár FK, a ve struktuře databáze řeknete, že „tady je záznam protokolu, na který odkazuje FK, například na skupinu záznamů“, tak když ho vložíte, PostgreSQL nezbývá nic jiného, ​​než jak vzít a poctivě provést SELECT 1 FROM master_fk1_table WHERE ... s identifikátorem, který se pokoušíte vložit - jen pro kontrolu, zda je tam tento záznam přítomen, abyste tento cizí klíč svým vložením „neodlomili“.

Dostaneme místo jednoho záznamu v cílové tabulce a jejích indexech plus čtení ze všech tabulek, na které odkazuje. A my to vůbec nepotřebujeme – naším úkolem je nahrát co nejvíce a co nejrychleji s co nejmenší zátěží. Takže FK – dolů s tím!

Dalším bodem je agregace a hašování. Zpočátku jsme je implementovali do databáze - koneckonců je vhodné hned, když záznam dorazí, udělat ho v nějakém tabletu "plus jedna" přímo ve spoušti. Dobré, pohodlné, ale stejně špatné - vložíte jeden záznam, ale jste nuceni číst a zapisovat něco jiného z jiné tabulky. Navíc nejen číst a psát – také to dělat pokaždé.

Nyní si představte, že máte tabulku, ve které jednoduše spočítáte počet požadavků, které prošly konkrétním hostitelem: +1, +1, +1, ..., +1. A vy to v zásadě nepotřebujete - je to všechno možné součet v paměti na kolektoru a poslat na základnu jedním tahem +10.

Ano, v případě nějaké poruchy se vaše logická integrita může „rozpadnout“, ale to je téměř nereálný případ – protože máte normální server, má baterii v řadiči, máte protokol transakcí, přihlaste se do systému souborů ... Obecně to nestojí za to. Nestojí to za ztrátu výkonu, kterou získáte spuštěním triggerů/FK za náklady, které vám vzniknou.

Totéž platí pro hashování. Přiletí k vám určitý požadavek, vypočítáte z něj v databázi určitý identifikátor, zapíšete ho do databáze a pak to všem řeknete. Vše je v pořádku, dokud za vámi v době nahrávání nepřijde druhý člověk, který si to chce nahrát sám – a vy máte blokaci, a to už je špatné. Pokud tedy můžete přenést generování některých ID na klienta (vzhledem k základně), je lepší to udělat.

Prostě se nám osvědčilo použít MD5 z textu - poptávky, plánu, šablony,... Spočítáme to na straně kolektoru a hotové ID „nasypeme“ do databáze. Délka MD5 a denní dělení nás netrápí možnými kolizemi.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Ale abychom to všechno rychle nahráli, museli jsme upravit samotný postup nahrávání.

Jak se obvykle zapisují data? Máme nějakou datovou sadu, rozložíme ji na několik tabulek a pak KOPÍRUJEME - nejprve do první, pak do druhé, do třetí... Je to nepohodlné, protože se zdá, že zapisujeme jeden datový tok ve třech krocích za sebou. Nepříjemný. Dá se to udělat rychleji? Umět!

K tomu stačí pouze rozložit tyto toky paralelně mezi sebou. Ukazuje se, že máme chyby, požadavky, šablony, zámky létající v samostatných vláknech, ... - a to všechno píšeme paralelně. K tomu to stačí udržovat trvale otevřený COPY kanál pro každou jednotlivou cílovou tabulku.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Tedy sběratel vždy je tam proud, do kterého si mohu zapisovat data, která potřebuji. Ale aby databáze viděla tato data a někdo nevisel v zámcích a čekal, až budou tato data zapsána, COPY se musí v určitých intervalech přerušovat. Pro nás se jako nejefektivnější ukázala perioda v řádu 100ms - zavřeme a hned zase otevřeme ke stejnému stolu. A pokud nemáme dostatek jednoho proudu v některých vrcholech, pak sdružujeme až do určitého limitu.

Navíc jsme zjistili, že pro takový profil zatížení je jakákoliv agregace, kdy se záznamy shromažďují v paketech, zlo. Klasické zlo je INSERT ... VALUES a dalších 1000 záznamů. Protože v tu chvíli máte na médiu maximum zápisu a všichni ostatní, kteří se snaží něco zapsat na disk, budou čekat.

Abyste se zbavili takových anomálií, prostě nic neagregovat, vůbec nevyrovnávací paměť. A pokud dojde k ukládání do vyrovnávací paměti na disk (naštěstí vás o tom informuje Stream API Node.js) - toto připojení odložte. To vám pak přijde akce, že je zase volná – napište na ni z nahromaděné fronty. Mezitím je obsazeno - vezměte si další volný z bazénu a napište mu.

Před implementací tohoto přístupu k zápisu dat jsme měli asi 4K zápisové operace a tímto způsobem jsme snížili zátěž 4krát. Nyní se díky novým monitorovaným databázím rozrostly o dalších 6x - až 100MB/s. A nyní ukládáme logy za poslední 3 měsíce v objemu cca 10-15TB a doufáme, že jakýkoli vývojář dokáže vyřešit jakýkoli problém za tři měsíce.

Rozumíme problémům

Ale pouhé shromažďování všech těchto dat je dobré, užitečné, vhodné, ale nestačí – musíte tomu rozumět. Protože to jsou miliony různých plánů denně.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Ale miliony jsou nekontrolovatelné, musíte nejprve udělat "menší". A v první řadě se musíte rozhodnout, jak tento „menší“ uspořádáte.

Identifikovali jsme pro sebe tři klíčové body:

  • kdo odeslal tuto žádost
    Tedy z jaké aplikace „přišel“: webové rozhraní, backend, platební systém nebo něco jiného.
  • kde se to stalo
    Na jakém konkrétním serveru. Protože pokud máte pod jednou aplikací více serverů a najednou se jeden „otupe“ (protože „disk je shnilý“, „vytekla paměť“, nějaký jiný problém), pak je potřeba konkrétně oslovit server.
  • как problém se projevil tak či onak

Abychom pochopili, „kdo“ nám poslal požadavek, používáme běžný nástroj – nastavení proměnné relace: SET application_name = '{bl-host}:{bl-method}'; - zasíláme název hostitele obchodní logiky, ze které požadavek pochází, a název metody nebo aplikace, která jej iniciovala.

Poté, co jsme předali "master" požadavku, musí být zobrazen v protokolu - k tomu konfigurujeme proměnnou log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Koho to možná zajímá podívejte se do manuáluco to všechno znamená. Ukazuje se, že v protokolu vidíme:

  • čas
  • ID procesů a transakcí
  • základní jméno
  • IP osoby, která tuto žádost odeslala
  • a název metody

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Dále jsme si uvědomili, že není příliš zajímavé sledovat korelaci jednoho požadavku mezi různými servery. Zřídka se dostanete do situace, kdy máte jednu aplikaci stejně „svinstvo“ tu a tam. Ale i když to samé - podívejte se na některý z těchto serverů.

Takže, řez "jeden server - jeden den" našli jsme dost pro jakoukoli analýzu.

První analytická část je stejná "vzorek" - zkrácená forma prezentace plánu, zbavená všech číselných ukazatelů. Druhý řez je aplikace nebo metoda a třetí je konkrétní uzel plánu, který nám způsobil problémy.

Když jsme přešli od konkrétních instancí k šablonám, získali jsme dvě výhody najednou:

  • mnohonásobné snížení počtu objektů pro analýzu
    Problém už musíme analyzovat ne podle tisíců požadavků nebo plánů, ale podle desítek vzorů.
  • Časová osa
    To znamená, že shrnutím „faktů“ v určité části lze zobrazit jejich vzhled během dne. A tady můžete pochopit, že pokud máte nějaký vzorec, který se stane například jednou za hodinu, ale měl by - jednou denně, přemýšlet o tom, co se pokazilo - kým a proč to bylo nazváno, možná by to mělo být tady neměl by. Toto je další nenumerický, čistě vizuální způsob analýzy.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Zbývající metody jsou založeny na ukazatelích, které vyjmeme z plánu: kolikrát se takový vzor vyskytl, celkový a průměrný čas, kolik dat bylo načteno z disku a kolik z paměti ...

Protože se například dostanete na stránku analýzy hostitele, podívejte se – na disku je toho na čtení příliš mnoho. Disk na serveru to nezvládne - a kdo z něj čte?

A můžete seřadit podle libovolného sloupce a rozhodnout se, co budete řešit právě teď - se zátěží procesoru nebo na disku, nebo s celkovým počtem požadavků ... Seřazeno, podíval se na ty "top", opraveno - spustila novou verzi aplikace.
[video přednáška]

A okamžitě můžete vidět různé aplikace, které jdou se stejnou šablonou z požadavku jako SELECT * FROM users WHERE login = 'Vasya'. Frontend, backend, zpracování... A vy se divíte, proč by zpracování mělo číst uživatel, když s ním neinteraguje.

Opačným způsobem je z aplikace okamžitě vidět, co dělá. Frontend je například toto, toto, toto a toto jednou za hodinu (pomáhá jen časová osa). A hned vyvstává otázka - zdá se, že není věcí frontendu dělat něco jednou za hodinu ...

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Po nějaké době jsme si uvědomili, že nám chybí agregace statistiky podle uzlů plánu. Z plánů jsme izolovali pouze ty uzly, které něco dělají s daty samotných tabulek (čtou/zapisují je podle indexu nebo ne). Ve skutečnosti je s ohledem na předchozí obrázek přidán pouze jeden aspekt - kolik záznamů nám tento uzel přinesla kolik bylo vyřazeno (řádky odstraněné filtrem).

Nemáte na štítku vhodný index, uděláte na něj požadavek, proletí kolem indexu, spadne do Seq Scan ... vyfiltroval jste všechny záznamy kromě jednoho. A proč potřebujete 100 milionů filtrovaných záznamů denně, není lepší index srolovat?

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Po analýze všech plánů podle uzlů jsme si uvědomili, že v plánech jsou některé typické struktury, které s největší pravděpodobností vypadají podezřele. A bylo by hezké říci vývojáři: „Příteli, zde nejprve čtete podle indexu, pak třídíte a pak odříznete“ - zpravidla je zde jeden záznam.

Každý, kdo psal žádosti, se pravděpodobně setkal s takovým vzorem: „Dejte mi poslední objednávku pro Vasyu, její datum.“ A pokud nemáte index podle data nebo v indexu, který jste použili, žádné datum není, pak krok přesně na takové „hrábě“ .

Ale víme, že se jedná o „hrabání“ – tak proč rovnou neříkat vývojáři, co by měl udělat. Při otevření plánu nyní náš vývojář okamžitě uvidí krásný obrázek s nápovědami, kde je mu okamžitě řečeno: „Máte problémy tu a tam, ale řeší se tak a tak.“

V důsledku toho množství zkušeností, které bylo potřeba k řešení problémů na začátku a nyní, výrazně kleslo. Tady máme takový nástroj.

Hromadná optimalizace PostgreSQL dotazů. Kirill Borovikov (Tensor)

Zdroj: www.habr.com

Přidat komentář