Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Poročilo predstavlja nekaj pristopov, ki omogočajo spremljati delovanje poizvedb SQL, ko jih je na milijone na danin na stotine nadzorovanih strežnikov PostgreSQL.

Katere tehnične rešitve nam omogočajo učinkovito obdelavo takšne količine informacij in kako to olajša življenje navadnemu razvijalcu?


koga zanima? analiza specifičnih problemov in različne optimizacijske tehnike Poizvedbe SQL in reševanje tipičnih težav DBA v PostgreSQL - lahko tudi preberi vrsto člankov na to temo.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)
Moje ime je Kirill Borovikov, predstavljam Podjetje Tensor. Konkretno sem specializiran za delo z bazami podatkov v našem podjetju.

Danes vam bom povedal, kako optimiziramo poizvedbe, ko vam ni treba "ločiti" uspešnosti ene same poizvedbe, ampak rešiti težavo množično. Ko je na milijone prošenj in jih morate nekaj najti pristopi k rešitvi ta velik problem.

Na splošno je Tensor za milijon naših strank VLSI je naša aplikacija: korporativno socialno omrežje, rešitve za video komunikacijo, za notranji in eksterni pretok dokumentov, računovodski sistemi za računovodstvo in skladišča,... Se pravi tak “megakombajn” za celostno vodenje poslovanja, v katerem je več kot 100 različnih notranji projekti.

Da vsi normalno delujejo in se razvijajo, imamo 10 razvojnih centrov po vsej državi, v njih pa še več 1000 razvijalcev.

S PostgreSQL delamo od leta 2008 in smo zbrali veliko količino tega, kar obdelujemo - podatkov o strankah, statističnih, analitičnih, podatkov iz zunanjih informacijskih sistemov - več kot 400TB. Samo v produkciji je približno 250 strežnikov, skupaj pa je okoli 1000 podatkovnih strežnikov, ki jih spremljamo.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

SQL je deklarativni jezik. Ne opisujete, "kako" bi moralo nekaj delovati, ampak "kaj" želite doseči. DBMS ve bolje, kako narediti JOIN - kako povezati vaše tabele, katere pogoje postaviti, kaj bo šlo skozi indeks, kaj ne ...

Nekateri DBMS sprejemajo namige: "Ne, poveži ti dve tabeli v takšno in tako čakalno vrsto," vendar PostgreSQL tega ne more storiti. To je zavestno stališče vodilnih razvijalcev: "Raje dokončamo optimizator poizvedb, kot da bi razvijalcem dovolili uporabo nekakšnih namigov."

Toda kljub temu, da PostgreSQL ne dovoli "zunanjemu" nadzora nad samim seboj, popolnoma dopušča poglej, kaj se dogaja v njemko zaženete svojo poizvedbo in kje ima težave.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Na splošno, s kakšnimi klasičnimi težavami se običajno srečuje razvijalec [za DBA]? »Tukaj smo izpolnili zahtevo in pri nas gre vse počasi, vse visi, nekaj se dogaja ... Nekakšna težava!«

Razlogi so skoraj vedno enaki:

  • neučinkovit algoritem poizvedb
    Razvijalec: "Zdaj mu dam 10 tabel v SQL prek JOIN ..." - in pričakuje, da bodo njegovi pogoji čudežno učinkovito "odvezani" in bo vse dobil hitro. Toda čudeži se ne dogajajo in vsak sistem s tako spremenljivostjo (10 tabel v enem FROM) vedno povzroči kakšno napako. [članek]
  • nepomembna statistika
    Ta točka je zelo pomembna posebej za PostgreSQL, ko "vlijete" velik nabor podatkov na strežnik, naredite zahtevo in ta "sexcanits" vašo tablico. Kajti včeraj je bilo v njem 10 zapisov, danes pa 10 milijonov, vendar se PostgreSQL tega še ne zaveda in mu moramo o tem povedati. [članek]
  • »priklop« na vire
    Namestili ste veliko in močno obremenjeno bazo podatkov na šibek strežnik, ki nima dovolj zmogljivosti diska, pomnilnika ali procesorja. In to je vse ... Nekje je zgornja meja uspešnosti, nad katero ne moreš več skočiti.
  • blokiranje
    To je težka točka, vendar so najbolj pomembne za različne poizvedbe za spreminjanje (INSERT, UPDATE, DELETE) - to je ločena velika tema.

Pridobivanje načrta

...In za vse ostalo mi potrebujem načrt! Videti moramo, kaj se dogaja znotraj strežnika.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Načrt izvajanja poizvedbe za PostgreSQL je drevo algoritma izvajanja poizvedbe v besedilni predstavitvi. Ravno algoritem se je po analizi načrtovalca izkazal za najučinkovitejšega.

Vsako drevesno vozlišče je operacija: pridobivanje podatkov iz tabele ali indeksa, izdelava bitne slike, združevanje dveh tabel, združevanje, sekanje ali izključevanje izbir. Izvajanje poizvedbe vključuje sprehod skozi vozlišča tega drevesa.

Da bi dobili načrt poizvedbe, je najlažji način, da izvedete stavek EXPLAIN. Dobiti z vsemi resničnimi atributi, to je dejansko izvesti poizvedbo na osnovi - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Slab del: ko ga zaženete, se zgodi "tukaj in zdaj", zato je primeren samo za lokalno odpravljanje napak. Če vzamete zelo obremenjen strežnik, ki je pod močnim pretokom podatkovnih sprememb, in vidite: »Oh! Tukaj imamo počasno izvedbosya prošnja." Pred pol ure, pred eno uro – ko ste izvajali in pridobivali to zahtevo iz dnevnikov ter jo prenašali nazaj na strežnik, se je vaš celoten nabor podatkov in statistika spremenil. Zaženete ga za odpravljanje napak - in deluje hitro! In ne moreš razumeti zakaj, zakaj je bilo počasi.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Da bi razumeli, kaj se je točno zgodilo v trenutku, ko je bila zahteva izvršena na strežniku, so pisali pametni ljudje modul auto_explain. Prisoten je v skoraj vseh najpogostejših distribucijah PostgreSQL in ga je mogoče preprosto aktivirati v konfiguracijski datoteki.

Če ugotovi, da se neka zahteva izvaja dlje od omejitve, ki ste ji jo naročili, to stori »posnetek« načrta te zahteve in ju skupaj zapiše v dnevnik.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Zdi se, da je zdaj vse v redu, gremo do hloda in vidimo tam ... [besedilo krpica]. Toda o njem ne moremo reči ničesar, razen dejstva, da je odličen načrt, saj je za njegovo izvedbo trajalo 11 ms.

Zdi se, da je vse v redu – a nič ni jasno, kaj se je pravzaprav zgodilo. Razen splošnega časa pravzaprav ne vidimo ničesar. Ker gledanje takšnega "jagnjeta" navadnega besedila na splošno ni vizualno.

Toda tudi če ni očitno, tudi če je neprijetno, obstajajo bolj temeljne težave:

  • Vozlišče označuje vsota virov celotnega poddrevesa pod njim. To pomeni, da ne morete samo ugotoviti, koliko časa je bilo porabljenega za to določeno skeniranje indeksa, če je pod njim ugnezdeno stanje. Dinamično moramo pogledati, ali so notri »otroci« in pogojne spremenljivke, CTE - in vse to odšteti »v naših glavah«.
  • Druga točka: čas, ki je naveden na vozlišču, je čas izvajanja enega vozlišča. Če je bilo to vozlišče izvedeno kot rezultat, na primer, večkratnega zanke skozi zapise tabele, potem se število zank – ciklov tega vozlišča – v načrtu poveča. Toda sam čas atomske izvedbe ostaja enak glede na načrt. To pomeni, da bi razumeli, kako dolgo je bilo to vozlišče skupaj izvedeno, morate eno stvar pomnožiti z drugo - spet "v glavi."

V takih situacijah razumejte "Kdo je najšibkejši člen?" skoraj nemogoče. Zato tudi razvijalci sami pišejo v "priročniku", da “Razumeti načrt je umetnost, ki se je je treba naučiti, izkusiti...”.

Vendar imamo 1000 razvijalcev in te izkušnje ne morete prenesti na vsakega od njih. Jaz, ti, on ve, ampak nekdo tam ne ve več. Mogoče se bo naučil, morda pa tudi ne, ampak zdaj mora delati - in kje bi dobil te izkušnje?

Vizualizacija načrta

Zato smo spoznali, da za reševanje teh težav potrebujemo dobra vizualizacija načrta. [Članek]

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Najprej smo šli »skozi trg« - poglejmo na internet, kaj sploh obstaja.

Izkazalo pa se je, da je razmeroma "živih" rešitev, ki se bolj ali manj razvijajo, zelo malo - dobesedno le ena: pojasni.depesz.com avtorja Hubert Lubaczewski. Ko v polje »feed« vnesete besedilno predstavitev načrta, vam prikaže tabelo z razčlenjenimi podatki:

  • lastni čas obdelave vozlišča
  • skupni čas za celotno poddrevo
  • število zapisov, ki so bili pridobljeni in so bili statistično pričakovani
  • samo telo vozlišča

Ta storitev ima tudi možnost deljenja arhiva povezav. Tja si vrgel svoj načrt in rekel: "Hej, Vasja, tukaj je povezava, tam je nekaj narobe."

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

So pa tudi majhne težave.

Prvič, ogromno "copy-paste". Vzameš kos polena, ga zapičiš tja in še enkrat in spet.

Drugič, ni analize količine prebranih podatkov — isti medpomnilniki kot izhod EXPLAIN (ANALYZE, BUFFERS), tega ne vidimo tukaj. Preprosto jih ne zna razstaviti, razumeti in delati z njimi. Ko berete veliko podatkov in se zavedate, da morda napačno razporejate disk in predpomnilnik pomnilnika, so te informacije zelo pomembne.

Tretja negativna točka je zelo šibek razvoj tega projekta. Povezave so zelo majhne, ​​dobro je enkrat na šest mesecev, koda pa je v Perlu.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Ampak to je vse “lirika”, s tem bi lahko nekako živeli, ampak ena stvar nas je močno odvrnila od te storitve. To so napake pri analizi Common Table Expression (CTE) in različnih dinamičnih vozlišč, kot je InitPlan/SubPlan.

Če verjamete tej sliki, je skupni čas izvajanja vsakega posameznega vozlišča večji od skupnega časa izvajanja celotne zahteve. Preprosto je - čas generiranja tega CTE ni bil odštet od vozlišča CTE Scan. Zato ne poznamo več pravilnega odgovora, koliko časa je trajal sam pregled CTE.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Potem smo ugotovili, da je čas, da napišemo svoje - hura! Vsak razvijalec pravi: "Zdaj bomo napisali svoje, to bo zelo enostavno!"

Vzeli smo sklad, značilen za spletne storitve: jedro, ki temelji na Node.js + Express, uporabili smo Bootstrap in D3.js za čudovite diagrame. In naša pričakovanja so bila popolnoma upravičena - prvi prototip smo prejeli v 2 tednih:

  • razčlenjevalnik načrta po meri
    Se pravi, zdaj lahko razčlenimo kateri koli načrt od tistih, ki jih ustvari PostgreSQL.
  • pravilna analiza dinamičnih vozlišč - CTE Scan, InitPlan, SubPlan
  • analiza porazdelitve medpomnilnikov - kje se podatkovne strani berejo iz pomnilnika, kje iz lokalnega predpomnilnika, kje z diska
  • dobil jasnost
    Da ne bi vsega tega "kopali" po dnevniku, ampak da bi takoj na sliki videli "najšibkejši člen".

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Imamo nekaj takega, z vključenim označevanjem sintakse. Vendar običajno naši razvijalci ne delajo več s popolno predstavitvijo načrta, ampak s krajšo. Konec koncev smo vse številke že razčlenili in jih metali levo in desno, na sredini pa pustili samo prvo vrstico, za kakšno vozlišče gre: CTE Scan, CTE generacija ali Seq Scan po nekem znaku.

To je skrajšana predstavitev, ki jo imenujemo predlogo načrta.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Kaj bi bilo še priročno? Bilo bi priročno videti, kolikšen delež našega skupnega časa je dodeljen kateremu vozlišču - in ga preprosto »odložiti« ob stran krožni diagram.

Kažemo na vozlišče in vidimo - izkazalo se je, da je Seq Scan vzel manj kot četrtino celotnega časa, preostale 3/4 pa je opravil CTE Scan. groza! To je majhna opomba o "hitrosti ognja" CTE Scan, če jih aktivno uporabljate v svojih poizvedbah. Niso zelo hitri - slabši so celo od običajnega skeniranja tabele. [Članek] [Članek]

Ampak običajno so takšni diagrami bolj zanimivi, bolj kompleksni, ko takoj pokažemo na segment in vidimo, da je na primer več kot polovico časa kakšen Seq Scan “pojedel”. Poleg tega je bil notri nekakšen filter, v skladu z njim je bilo zavrženih veliko zapisov ... To sliko lahko neposredno vržete razvijalcu in rečete: »Vasja, tukaj je zate vse slabo! Ugotovi, poglej – nekaj ni v redu!«

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Seveda je bilo vmes tudi nekaj »grabljic«.

Prva stvar, na katero smo naleteli, je bil problem zaokroževanja. Čas vsakega posameznega vozlišča v načrtu je prikazan z natančnostjo 1 μs. In ko število ciklov vozlišč preseže na primer 1000 - po izvedbi PostgreSQL razdeli "znotraj natančnosti", potem pri izračunu nazaj dobimo skupni čas "nekje med 0.95 ms in 1.05 ms". Ko gre štetje na mikrosekunde, je to v redu, ko pa je že [mili]sekund, morate to informacijo upoštevati, ko »odvezujete« vire na vozlišča načrta »kdo je koliko porabil«.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Druga točka, bolj zapletena, je porazdelitev virov (tistih medpomnilnikov) med dinamičnimi vozlišči. To nas je stalo prva 2 tedna prototipa in še 4 tedne.

Precej enostavno je dobiti to vrsto težave - naredimo CTE in v njem domnevno nekaj preberemo. Pravzaprav je PostgreSQL "pameten" in tam ne bo prebral ničesar neposredno. Nato iz njega vzamemo prvi zapis in njemu sto in prvega iz istega CTE.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Pogledamo načrt in razumemo - čudno je, imamo 3 medpomnilnike (podatkovne strani), ki so "porabljeni" v Seq Scan, še 1 v CTE Scan in 2 več v drugem CTE Scan. Se pravi, če preprosto vse seštejemo, dobimo 6, s tablice pa preberemo le 3! CTE Scan ne bere ničesar od nikoder, ampak deluje neposredno s pomnilnikom procesa. Se pravi, tukaj očitno nekaj ni v redu!

Pravzaprav se izkaže, da so tukaj vse tiste 3 strani podatkov, ki so bile zahtevane od Seq Scan, najprej je 1 zahteval 1. CTE Scan, potem pa 2. in še 2 sta mu bili prebrani. Se pravi, skupaj Prebrani podatki so bili 3 strani, ne 6.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

In ta slika nas je pripeljala do razumevanja, da izvedba načrta ni več drevo, ampak preprosto nekakšen aciklični graf. In dobili smo takšen diagram, da razumemo, "od kod kaj je sploh prišlo." Se pravi, tukaj smo ustvarili CTE iz pg_class in ga zahtevali dvakrat, skoraj ves naš čas pa smo porabili v veji, ko smo ga vprašali drugič. Jasno je, da je branje 2. vnosa veliko dražje kot samo branje 101. vnosa s tablice.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Nekaj ​​časa sva izdihovala. Rekli so: »Zdaj, Neo, veš kung fu! Zdaj je naša izkušnja neposredno na vašem zaslonu. Zdaj ga lahko uporabite." [Članek]

Konsolidacija dnevnika

Naših 1000 razvijalcev si je oddahnilo. Vendar smo razumeli, da imamo samo na stotine "bojnih" strežnikov in vse to "copy-paste" s strani razvijalcev sploh ni priročno. Ugotovili smo, da ga moramo zbrati sami.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Na splošno obstaja standardni modul, ki lahko zbira statistiko, vendar ga je treba tudi aktivirati v konfiguraciji - to modul pg_stat_statements. Vendar nam ni ustrezal.

Prvič, dodeli istim poizvedbam z uporabo različnih shem znotraj iste zbirke podatkov različni ID-ji poizvedb. To je, če najprej storite SET search_path = '01'; SELECT * FROM user LIMIT 1;in potem SET search_path = '02'; in isto zahtevo, potem bo statistika tega modula imela različne zapise in ne bom mogel zbrati splošne statistike posebej v kontekstu tega profila zahteve, ne da bi upošteval sheme.

Druga točka, ki nam je preprečila uporabo, je pomanjkanje načrtov. Se pravi, ni načrta, obstaja samo zahteva sama. Vidimo, kaj se upočasnjuje, vendar ne razumemo, zakaj. In tu se vrnemo k problemu hitro spreminjajočega se nabora podatkov.

In zadnji trenutek - pomanjkanje "dejstev". To pomeni, da ne morete obravnavati določenega primera izvajanja poizvedbe - ni ga, obstajajo samo združeni statistični podatki. Čeprav je s tem mogoče delati, je le zelo težko.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Zato smo se odločili za boj proti copy-paste in začeli pisati zbiralec.

Zbiralnik se poveže preko SSH, s certifikatom vzpostavi varno povezavo s strežnikom z bazo ter tail -F »prilepi« nanj v dnevniški datoteki. Torej v tej seji dobimo popolno »zrcalo« celotne datoteke dnevnika, ki jih ustvari strežnik. Obremenitev samega strežnika je minimalna, saj tam ne razčlenjujemo ničesar, samo zrcalimo promet.

Ker smo že začeli pisati vmesnik v Node.js, smo nadaljevali s pisanjem zbiralnika v njem. In ta tehnologija se je upravičila, saj je zelo priročno uporabljati JavaScript za delo s šibko oblikovanimi besedilnimi podatki, kar je dnevnik. Sama infrastruktura Node.js kot zaledna platforma vam omogoča enostavno in priročno delo z omrežnimi povezavami in pravzaprav s kakršnimi koli podatkovnimi tokovi.

V skladu s tem "raztegnemo" dve povezavi: prvo, da "poslušamo" sam dnevnik in ga vzamemo k sebi, drugo pa občasno vprašamo bazo. "Ampak dnevnik kaže, da je znak z oid 123 blokiran," vendar to razvijalcu ne pomeni ničesar, zato bi bilo lepo vprašati zbirko podatkov: "Kaj sploh je OID = 123?" In tako bazo občasno vprašamo, česar še ne vemo o sebi.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

»Samo ene stvari niste upoštevali, obstaja vrsta čebel podobnih slonom!..« Ta sistem smo začeli razvijati, ko smo želeli nadzorovati 10 strežnikov. Najbolj kritično po našem razumevanju, kjer so se pojavile težave, s katerimi se je bilo težko spopasti. Toda v prvem kvartalu smo prejeli sto za spremljanje - ker je sistem deloval, vsi so ga želeli, vsem je bilo udobno.

Vse to je treba sešteti, pretok podatkov je velik in aktiven. Pravzaprav uporabljamo tisto, kar spremljamo, s čimer se lahko ukvarjamo. PostgreSQL uporabljamo tudi kot shrambo podatkov. In nič hitreje ne "vlije" podatkov vanj kot operater COPY Ne še.

Toda preprosto »prelivanje« podatkov v resnici ni naša tehnologija. Ker če imate približno 50k zahtev na sekundo na sto strežnikih, bo to ustvarilo 100-150GB dnevnikov na dan. Zato smo morali skrbno "odrezati" osnovo.

Prvič, smo delitev po dnevu, ker korelacija med dnevi na splošno nikogar ne zanima. Kakšna je razlika, kaj ste imeli včeraj, če ste nocoj uvedli novo različico aplikacije - in že nekaj nove statistike.

Drugič, naučili smo se (bili prisiljeni) zelo, zelo hitro za pisanje z uporabo COPY. Se pravi, ne samo COPYker je hitrejši od INSERT, in še hitreje.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Tretja točka - moral sem opustitev sprožilcev oziroma tujih ključev. To pomeni, da sploh nimamo referenčne integritete. Kajti če imate tabelo, ki ima par FK-jev, in v strukturi baze podatkov poveste, da je "tukaj zapis dnevnika, na katerega se FK sklicuje na primer na skupino zapisov," potem ko ga vstavite, PostgreSQL ne preostane drugega kot to, kako to vzeti in narediti pošteno SELECT 1 FROM master_fk1_table WHERE ... z identifikatorjem, ki ga poskušate vstaviti - samo zato, da preverite, ali je ta zapis prisoten tam, da ne "odlomite" tega tujega ključa z vstavitvijo.

Namesto enega zapisa v ciljno tabelo in njene indekse dobimo dodatno prednost branja iz vseh tabel, na katere se nanaša. A tega sploh ne potrebujemo - naša naloga je posneti čim več in čim hitreje z najmanjšo obremenitvijo. Torej FK - dol!

Naslednja točka je združevanje in zgoščevanje. Sprva smo jih implementirali v bazo podatkov - navsezadnje je priročno, da takoj, ko prispe zapis, to storite v nekakšni tablici "plus ena" desno v sprožilec. No, priročno je, a enako slabo - vstavite en zapis, vendar ste prisiljeni brati in pisati nekaj drugega iz druge tabele. Poleg tega ne samo, da berete in pišete, to tudi počnete vsakič.

Zdaj pa si predstavljajte, da imate tabelo, v kateri preprosto preštejete število zahtev, ki so prešle skozi določenega gostitelja: +1, +1, +1, ..., +1. In tega načeloma ne potrebujete - vse je mogoče vsota v pomnilniku na zbiralniku in pošlje v bazo podatkov naenkrat +10.

Da, v primeru kakšnih težav se ti lahko "podre" logična celovitost, vendar je to skoraj nerealen primer - ker imaš običajen strežnik, ima baterijo v krmilniku, imaš dnevnik transakcij, dnevnik na datotečni sistem ... Na splošno ni vredno. Izguba produktivnosti, ki jo dobite zaradi izvajanja sprožilcev/FK, ni vredna stroškov, ki jih imate.

Enako je z zgoščevanjem. Do tebe prileti določena zahteva, iz nje izračunaš določen identifikator v bazi, jo zapišeš v bazo in potem vsem poveš. Vse je v redu, dokler v času snemanja ne pride do vas druga oseba, ki želi posneti isto - in vas blokirajo, to pa je že slabo. Torej, če lahko prenesete ustvarjanje nekaterih ID-jev na odjemalca (glede na bazo podatkov), je bolje, da to storite.

Prav odlično nam je šlo, da smo uporabili MD5 iz teksta - zahteve, načrta, predloge,... Izračunamo ga na strani zbiralnika, in že pripravljen ID “zlijemo” v bazo. Dolžina MD5 in dnevna particija nam omogočata, da ne skrbimo za morebitne kolizije.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

A da bi vse to hitro posneli, smo morali spremeniti sam postopek snemanja.

Kako običajno pišete podatke? Imamo nekakšen nabor podatkov, ga razdelimo na več tabel, nato pa ga KOPIRAMO - najprej v prvo, nato v drugo, v tretjo ... Neprijetno je, ker se zdi, da pišemo en tok podatkov v treh korakih. zaporedno. Neprijeten. Je mogoče hitreje? Lahko!

Če želite to narediti, je dovolj, da te tokove razstavite vzporedno drug z drugim. Izkazalo se je, da imamo napake, zahteve, predloge, blokade, ... ki letijo v ločenih nitih - in vse to pišemo vzporedno. Dovolj za to imejte kanal COPY stalno odprt za vsako posamezno ciljno tabelo.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Se pravi pri kolektorju vedno je tok, v katerega lahko zapišem podatke, ki jih potrebujem. Da pa baza podatkov vidi te podatke in da se nekdo ne zatakne pri čakanju, da se ti podatki zapišejo, KOPIRANJE je treba v določenih intervalih prekiniti. Pri nas je bila najučinkovitejša doba cca 100ms - zapremo in takoj spet odpremo na isto tabelo. In če v nekaterih konicah nimamo dovolj enega toka, potem združujemo do določene meje.

Dodatno smo ugotovili, da je za takšen profil obremenitve vsako združevanje, ko se zapisi zbirajo v paketih, zlo. Klasično zlo je INSERT ... VALUES in še 1000 zapisov. Ker imate na tej točki vrh zapisovanja na medij in vsi ostali, ki poskušajo nekaj zapisati na disk, bodo čakali.

Da bi se znebili takšnih anomalij, preprosto ne seštevajte ničesar, sploh ne blaži. In če pride do medpomnjenja na disk (na srečo Stream API v Node.js omogoča, da to ugotovite) - odložite to povezavo. Ko prejmete dogodek, da je spet prost, mu pišite iz nabrane čakalne vrste. In ko je zaseden, vzemite naslednjega prostega iz bazena in mu pišite.

Pred uvedbo tega pristopa k zapisovanju podatkov smo imeli približno 4K pisalne operacije in na ta način smo obremenitev zmanjšali za 4-krat. Sedaj so zaradi novih nadzorovanih podatkovnih baz zrasli še za 6-krat - do 100MB/s. In zdaj shranjujemo dnevnike za zadnje 3 mesece v prostornini približno 10-15 TB, v upanju, da bo v samo treh mesecih kateri koli razvijalec lahko rešil kakršno koli težavo.

Razumemo težave

A preprosto zbiranje vseh teh podatkov je dobro, uporabno, relevantno, a ne dovolj – treba ga je razumeti. Ker gre za milijone različnih načrtov na dan.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Toda milijoni so neobvladljivi, najprej moramo narediti »manjše«. In najprej se morate odločiti, kako boste organizirali to "manjšo" stvar.

Identificirali smo tri ključne točke:

  • ki poslal to zahtevo
    Se pravi, iz katere aplikacije je »prispelo«: spletnega vmesnika, zaledja, plačilnega sistema ali česa drugega.
  • če to se je zgodilo
    Na katerem konkretnem strežniku? Kajti če imate več strežnikov pod eno aplikacijo in nenadoma eden "ponori" (ker je "disk pokvarjen", "pušča pomnilnik", kakšna druga težava), potem morate posebej nasloviti strežnik.
  • kot problem se je tako ali drugače manifestiral

Da bi razumeli, "kdo" nam je poslal zahtevo, uporabljamo standardno orodje - nastavitev spremenljivke seje: SET application_name = '{bl-host}:{bl-method}'; — pošljemo ime gostitelja poslovne logike, s katerega prihaja zahteva, in ime metode ali aplikacije, ki jo je sprožila.

Ko posredujemo »lastnika« zahteve, jo je treba izpisati v dnevnik - za to konfiguriramo spremenljivko log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Za tiste, ki jih zanima, mogoče poglej v priročnikkaj vse to pomeni. Izkazalo se je, da v dnevniku vidimo:

  • čas
  • identifikatorje procesov in transakcij
  • ime baze podatkov
  • IP osebe, ki je poslala to zahtevo
  • in ime metode

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Potem smo ugotovili, da ni zelo zanimivo gledati korelacije za eno zahtevo med različnimi strežniki. Ni pogosto, da imate situacijo, ko ena aplikacija tu in tam enako zajebe. Toda tudi če je isti, poglejte katerega koli od teh strežnikov.

Torej, tukaj je rez "en strežnik - en dan" izkazalo se je, da nam je dovolj za kakršno koli analizo.

Prvi analitični del je enak "vzorec" - skrajšana oblika predstavitve načrta, očiščena vseh številčnih kazalcev. Drugi rez je aplikacija ali metoda, tretji rez pa specifično vozlišče načrta, ki nam je povzročilo težave.

Ko smo prešli s specifičnih primerkov na predloge, smo dobili dve prednosti hkrati:

  • večkratno zmanjšanje števila predmetov za analizo
    Težave ne moramo več analizirati na tisoče poizvedb ali načrtov, ampak na desetine predlog.
  • časovnica
    To pomeni, da lahko s povzetkom "dejstev" znotraj določenega razdelka prikažete njihov videz čez dan. In tukaj lahko razumete, da če imate nekakšen vzorec, ki se zgodi na primer enkrat na uro, vendar bi se moral zgoditi enkrat na dan, bi morali razmisliti, kaj je šlo narobe - kdo je to povzročil in zakaj, morda bi moralo biti tukaj ne bi smel. To je še ena nenumerična, povsem vizualna metoda analize.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Preostale metode temeljijo na indikatorjih, ki jih izluščimo iz načrta: kolikokrat se je tak vzorec pojavil, skupni in povprečni čas, koliko podatkov je bilo prebranih z diska in koliko iz pomnilnika ...

Ker na primer pridete na analitično stran za gostitelja, poglejte - nekaj začne brati preveč na disku. Disk na strežniku tega ne prenese - kdo bere z njega?

In lahko razvrstite po kateremkoli stolpcu in se odločite, s čim se boste zdaj ukvarjali - z obremenitvijo procesorja ali diska ali skupnim številom zahtev ... Razvrstili smo, pogledali "najboljše", popravili in uvedel novo različico aplikacije.
[video predavanje]

In takoj lahko vidite različne aplikacije, ki prihajajo z isto predlogo iz zahteve, kot je SELECT * FROM users WHERE login = 'Vasya'. Frontend, backend, processing... In sprašujete se, zakaj bi procesiranje bralo uporabnika, če z njim ne komunicira.

Nasprotno je, da iz aplikacije takoj vidite, kaj počne. Na primer, frontend je to, to, to in to enkrat na uro (časovnica pomaga). In takoj se pojavi vprašanje: zdi se, da ni naloga frontenda, da naredi nekaj enkrat na uro ...

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Čez nekaj časa smo ugotovili, da nam manjka agregacije statistika po vozliščih načrta. Iz načrtov smo izolirali samo tista vozlišča, ki nekaj delajo s podatki samih tabel (jih berejo/pišejo po indeksu ali ne). Pravzaprav je dodan samo en vidik glede na prejšnjo sliko - koliko zapisov nam je prineslo to vozlišče?in koliko jih je bilo zavrženih (vrstice, ki jih je filter odstranil).

Nimate ustreznega indeksa na tablici, naredite zahtevo po njem, zleti mimo indeksa, pade v Seq Scan ... izfiltrirali ste vse zapise razen enega. Zakaj potrebujete 100 milijonov filtriranih zapisov na dan? Ali ni bolje, da zvijete indeks?

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Ko smo analizirali vse načrte vozlišče za vozliščem, smo ugotovili, da je v načrtih nekaj tipičnih struktur, ki bodo zelo verjetno videti sumljive. In lepo bi bilo povedati razvijalcu: "Prijatelj, tukaj najprej bereš po indeksu, nato razvrstiš in nato odrežeš" - praviloma je en zapis.

Vsakdo, ki je pisal poizvedbe, je verjetno naletel na ta vzorec: »Daj mi zadnje naročilo za Vasjo, njegov datum.« In če nimate indeksa po datumu ali v indeksu, ki ste ga uporabili, ni datuma, potem boste stopite na popolnoma enake "grablje" .

Vendar vemo, da je to "grablje" - zakaj torej ne bi razvijalcu takoj povedali, kaj naj naredi. Skladno s tem, ko zdaj odpre načrt, naš razvijalec takoj vidi lepo sliko z nasveti, kjer mu takoj rečejo: "Tu in tam imaš težave, ki pa se rešujejo tako in tako."

Posledično se je količina izkušenj, ki so bile potrebne za reševanje problemov na začetku in zdaj, močno zmanjšala. To je orodje, ki ga imamo.

Množična optimizacija poizvedb PostgreSQL. Kirill Borovikov (Tenzor)

Vir: www.habr.com

Dodaj komentar