Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB

Nedávno som vám povedal, ako na to pomocou štandardných receptov zvýšiť výkon dotazov na čítanie SQL z databázy PostgreSQL. Dnes si povieme ako nahrávanie je možné vykonávať efektívnejšie v databáze bez použitia akýchkoľvek „zvratov“ v konfigurácii – jednoducho správnym usporiadaním dátových tokov.

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB

#1. Sekcie

Článok o tom, ako a prečo sa oplatí organizovať aplikované rozdelenie „teoreticky“ už bolo, tu si povieme o praxi uplatňovania niektorých prístupov v rámci nášho monitorovacia služba pre stovky PostgreSQL serverov.

"Veci uplynulých dní..."

Spočiatku, ako každý MVP, aj náš projekt začínal v pomerne nízkej záťaži - monitorovanie bolo realizované len pre desať najdôležitejších serverov, všetky tabuľky boli relatívne kompaktné... Postupom času sa však počet monitorovaných hostiteľov zvyšoval , a opäť sme sa pokúsili niečo urobiť s jedným z stoly s veľkosťou 1.5 TB, uvedomili sme si, že aj keď sa dá takto žiť ďalej, je to veľmi nepohodlné.

Časy boli takmer ako epické časy, rôzne verzie PostgreSQL 9.x boli relevantné, takže všetko rozdelenie bolo potrebné vykonať „ručne“ - cez tabuľkové dedičstvo a spúšťače smerovanie s dynamickým EXECUTE.

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB
Výsledné riešenie sa ukázalo ako dostatočne univerzálne na to, aby sa dalo preložiť do všetkých tabuliek:

  • Bola deklarovaná prázdna nadradená tabuľka „hlavička“, ktorá všetko popisovala potrebné indexy a spúšťače.
  • Záznam z pohľadu klienta bol urobený v „koreňovej“ tabuľke a interne pomocou smerovací spúšťač BEFORE INSERT záznam bol „fyzicky“ vložený do požadovanej sekcie. Ak ešte nič také nebolo, chytili sme výnimku a...
  • … používaním CREATE TABLE ... (LIKE ... INCLUDING ...) bola vytvorená na základe šablóny nadradenej tabuľky sekcii s obmedzením na požadovaný dátumaby pri načítaní údajov prebiehalo čítanie iba v nich.

PG10: prvý pokus

Rozdelenie na oddiely prostredníctvom dedenia však historicky nebolo vhodné na riešenie aktívneho zapisovacieho prúdu alebo veľkého počtu podradených oddielov. Napríklad si môžete spomenúť, že algoritmus na výber požadovanej sekcie mal kvadratická zložitosť, že to funguje so 100+ sekciami, sám chápeš ako...

V PG10 bola táto situácia výrazne optimalizovaná implementáciou podpory natívne rozdelenie. Okamžite sme sa ho preto pokúsili aplikovať ihneď po migrácii úložiska, ale...

Ako sa ukázalo po prehrabaní manuálu, natívne rozdelená tabuľka v tejto verzii je:

  • nepodporuje popisy indexov
  • nepodporuje spúšťače na ňom
  • nemôže byť nikoho „potomkom“
  • nepodporujú INSERT ... ON CONFLICT
  • nemôže automaticky vygenerovať sekciu

Po bolestivej rane hrabľami do čela sme si uvedomili, že bez úpravy aplikácie sa to nezaobíde a ďalší výskum sme odložili o šesť mesiacov.

PG10: druhá šanca

Začali sme teda riešiť problémy, ktoré sa vyskytli jeden po druhom:

  1. Pretože spúšťa a ON CONFLICT Zistili sme, že ich sem tam ešte potrebujeme, tak sme si urobili medzistupeň, aby sme ich vypracovali proxy tabuľka.
  2. Zbavil som sa „smerovania“ v spúšťačoch – teda od EXECUTE.
  3. Vytiahli to samostatne šablónová tabuľka so všetkými indexmitak, že nie sú ani prítomné v proxy tabuľke.

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB
Nakoniec sme po tomto všetkom natívne rozdelili hlavnú tabuľku. Vytvorenie novej sekcie je zatiaľ ponechané na svedomí aplikácie.

„Pílenie“ slovníkov

Ako v každom analytickom systéme, aj my sme mali "fakty" a "strihy" (slovníky). V našom prípade v tejto funkcii konali napr. telo šablóny podobné pomalé dopyty alebo samotný text dopytu.

„Fakty“ boli už dlho rozdelené podľa dňa, takže sme pokojne vymazali neaktuálne časti a neobťažovali nás (logy!). Ale vyskytol sa problém so slovníkmi...

Nehovoriac, že ​​ich bolo veľa, ale približne 100 TB „faktov“ viedlo k 2.5 TB slovníku. Z takejto tabuľky sa nedá pohodlne nič vymazať, nedá sa skomprimovať v primeranom čase a zápis do nej sa postupne spomalil.

Ako v slovníku... v ňom by mal byť každý záznam uvedený presne raz... a to je správne, ale!... Nikto nám nebráni mať samostatný slovník na každý deň! Áno, prináša to určitú redundanciu, ale umožňuje:

  • písať/čítať rýchlejšie kvôli menšej veľkosti sekcie
  • spotrebúvajú menej pamäte prácou s kompaktnejšími indexmi
  • ukladať menej údajov kvôli schopnosti rýchlo odstrániť zastarané

Výsledkom celého komplexu opatrení Zaťaženie procesora sa znížilo o ~30%, zaťaženie disku o ~50%:

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB
Zároveň sme pokračovali v zapisovaní presne toho istého do databázy, len s menšou záťažou.

#2. Evolúcia a refaktoring databázy

Tak sme sa dohodli na tom, čo máme každý deň má svoju časť s údajmi. Vlastne, CHECK (dt = '2018-10-12'::date) — a existuje rozdeľovací kľúč a podmienka, aby záznam spadal do konkrétnej sekcie.

Keďže všetky prehľady v našej službe sú zostavené v kontexte konkrétneho dátumu, indexy pre ne od „časov bez rozdelenia“ boli všetkých typov (server, dátum, Šablóna plánu), (server, dátum, Plánovací uzol), (dátum, Trieda chýb, Server), ...

Teraz však žijú na každom úseku vaše kópie každý takýto index... A v rámci každej sekcie dátum je konštanta... Ukazuje sa, že teraz sme v každom takomto indexe jednoducho zadajte konštantu ako jedno z polí, čím sa zväčší jeho objem aj čas jeho hľadania, ale neprinesie žiadny výsledok. Hrable si nechali pre seba, ups...

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB
Smer optimalizácie je zrejmý – jednoduchý odstráňte pole dátumu zo všetkých indexov na rozdelených tabuľkách. Vzhľadom na naše objemy je zisk približne 1 TB/týždeň!

Teraz si všimnime, že tento terabajt bolo treba ešte nejako zaznamenať. Teda aj my disk by sa mal teraz zaťažovať menej! Tento obrázok jasne ukazuje efekt získaný čistením, ktorému sme venovali týždeň:

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB

#3. „Rozloženie“ špičkového zaťaženia

Jedným z veľkých problémov zaťažených systémov je redundantná synchronizácia niektoré operácie, ktoré to nevyžadujú. Niekedy „lebo si to nevšimli“, niekedy „to bolo jednoduchšie“, ale skôr či neskôr sa toho musíte zbaviť.

Priblížime si predchádzajúci obrázok a uvidíme, že máme disk „pumpy“ pod záťažou s dvojnásobnou amplitúdou medzi susednými vzorkami, čo by sa jednoznačne „štatisticky“ nemalo stať pri takom počte operácií:

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB

To sa dá celkom ľahko dosiahnuť. S monitorovaním sme už začali takmer 1000 serverov, každé je spracované samostatným logickým vláknom a každé vlákno resetuje nahromadené informácie, ktoré sa majú odosielať do databázy s určitou frekvenciou, asi takto:

setInterval(sendToDB, interval)

Problém tu spočíva práve v tom, že všetky vlákna začínajú približne v rovnakom čase, takže ich časy odoslania sa takmer vždy zhodujú „do bodky“. Ups #2...

Našťastie sa to dá celkom ľahko opraviť, pridanie „náhodného“ rozbehu podľa času:

setInterval(sendToDB, interval * (1 + 0.1 * (Math.random() - 0.5)))

#4. Ukladáme do cache, čo potrebujeme

Tretím tradičným problémom vysokej záťaže je žiadna vyrovnávacia pamäť kde je mohol byť.

Napríklad sme umožnili analyzovať z hľadiska uzlov plánu (všetky tieto Seq Scan on users), ale hneď si myslia, že sú z väčšej časti rovnakí – zabudli.

Nie, samozrejme, do databázy sa znova nič nezapíše, tým sa preruší spúšť INSERT ... ON CONFLICT DO NOTHING. Tieto údaje sa však stále dostanú do databázy a sú zbytočné čítanie na kontrolu konfliktu musím urobiť. Ups #3...

Rozdiel v počte záznamov odoslaných do databázy pred/po povolení ukladania do vyrovnávacej pamäte je zrejmý:

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB

A toto je sprievodný pokles zaťaženia úložiska:

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB

Celkom

„Terabajt za deň“ znie strašidelne. Ak robíte všetko správne, potom je to správne 2^40 bajtov / 86400 12.5 sekúnd = ~XNUMX MB/sže držali aj skrutky desktopového IDE. 🙂

Ale vážne, aj pri desaťnásobnom „zošikmení“ záťaže počas dňa môžete ľahko splniť možnosti moderných SSD.

Píšeme v PostgreSQL na sublight: 1 hostiteľ, 1 deň, 1 TB

Zdroj: hab.com

Pridať komentár