Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Izvještaj predstavlja neke pristupe koji to dozvoljavaju prati performanse SQL upita kada ih ima na milione dnevno, a postoje stotine nadgledanih PostgreSQL servera.

Koja nam tehnička rješenja omogućavaju efikasno obrađivanje takve količine informacija i kako to olakšava život običnom programeru?


Ko je zainteresovan? analiza specifičnih problema i različite tehnike optimizacije SQL upiti i rješavanje tipičnih DBA problema u PostgreSQL - također možete pročitajte seriju članaka na ovu temu.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)
Moje ime je Kirill Borovikov, predstavljam Tenzor kompanija. Konkretno, specijalizovan sam za rad sa bazama podataka u našoj kompaniji.

Danas ću vam reći kako optimiziramo upite, kada ne morate "razdvojiti" performanse jednog upita, već masovno rješavati problem. Kada postoje milioni zahtjeva, a vi ih trebate pronaći pristupe rešenju ovaj veliki problem.

Generalno, Tensor za milion naših klijenata jeste VLSI je naša aplikacija: korporativna društvena mreža, rješenja za video komunikaciju, za interni i eksterni protok dokumenata, računovodstveni sistemi za računovodstvo i skladišta,... Odnosno, takav „mega-kombajn“ za integrisano upravljanje poslovanjem, u kojem postoji više od 100 različitih interni projekti.

Kako bismo osigurali da svi normalno rade i razvijaju se, imamo 10 razvojnih centara u cijeloj zemlji, s više njih 1000 programera.

Sa PostgreSQL-om radimo od 2008. godine i akumulirali smo veliku količinu onoga što obrađujemo - podataka o klijentima, statističkih, analitičkih, podataka iz eksternih informacionih sistema - više od 400TB. Samo je u proizvodnji oko 250 servera, a ukupno ima oko 1000 servera baze podataka koje pratimo.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

SQL je deklarativni jezik. Ne opisujete "kako" nešto treba da funkcioniše, već "šta" želite da postignete. DBMS bolje zna kako napraviti JOIN - kako povezati svoje tabele, koje uslove postaviti, šta će proći kroz indeks, šta neće...

Neki DBMS prihvataju nagoveštaje: „Ne, povežite ove dve tabele u taj i takav red“, ali PostgreSQL to ne može. Ovo je svjesna pozicija vodećih programera: “Radije bismo završili optimizaciju upita nego dozvolili programerima da koriste neke vrste savjeta.”

Ali, uprkos činjenici da PostgreSQL ne dozvoljava „spolja“ da se kontroliše, savršeno dozvoljava vidi šta se dešava u njemukada pokrenete svoj upit i gdje ima problema.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Općenito, s kojim klasičnim problemima obično dolazi programer [u DBA]? “Ovdje smo ispunili zahtjev, i kod nas sve ide sporo, sve visi, nešto se dešava... Neka nevolja!”

Razlozi su skoro uvek isti:

  • neefikasan algoritam upita
    Programer: "Sada mu dajem 10 tabela u SQL-u preko JOIN..." - i očekuje da će njegovi uslovi na čudesan način biti "razvezani" i da će sve brzo dobiti. Ali čuda se ne dešavaju i svaki sistem sa takvom varijabilnosti (10 tabela u jednom FROM-u) uvijek daje neku vrstu greške. [članak]
  • zastarjela statistika
    Ova tačka je veoma relevantna posebno za PostgreSQL, kada „sipate“ veliki skup podataka na server, napravite zahtev i on „seksira“ vaš tablet. Jer juče je u njemu bilo 10 zapisa, a danas ih ima 10 miliona, ali PostgreSQL još nije svjestan toga, a mi to moramo reći. [članak]
  • "priključiti" resurse
    Instalirali ste veliku i jako opterećenu bazu podataka na slabom serveru koji nema dovoljno performansi diska, memorije ili procesora. I to je sve... Negdje postoji plafon izvođenja iznad kojeg više ne možete skočiti.
  • blokiranje
    Ovo je teška stvar, ali oni su najrelevantniji za različite upite za modificiranje (INSERT, UPDATE, DELETE) - ovo je posebna velika tema.

Dobivanje plana

...A za sve ostalo mi treba plan! Moramo vidjeti šta se dešava unutar servera.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Plan izvršenja upita za PostgreSQL je stablo algoritma za izvršenje upita u tekstualnom predstavljanju. Upravo se algoritam, kao rezultat analize od strane planera, pokazao najefikasnijim.

Svaki čvor stabla je operacija: dohvaćanje podataka iz tabele ili indeksa, pravljenje bitmape, spajanje dve tabele, spajanje, presijecanje ili izuzimanje selekcija. Izvršavanje upita uključuje hodanje kroz čvorove ovog stabla.

Da biste dobili plan upita, najlakši način je da izvršite naredbu EXPLAIN. Da dobijete sve realne atribute, odnosno da stvarno izvršite upit na bazi - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Loš dio: kada ga pokrenete, to se dešava "ovdje i sada", tako da je pogodan samo za lokalno otklanjanje grešaka. Ako uzmete visoko opterećen server koji je pod jakim protokom promjena podataka, i vidite: „Oh! Ovdje imamo sporo izvršenjeся zahtjev." Prije pola sata, sat vremena - dok ste radili i dobijali ovaj zahtjev iz dnevnika, vraćali ga nazad na server, cijeli skup podataka i statistika su se promijenili. Pokrenete ga za otklanjanje grešaka - i radi brzo! I ne možete da razumete zašto, zašto bio polako.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Da bi shvatili šta se tačno desilo u trenutku kada je zahtev izvršen na serveru, pisali su pametni ljudi modul auto_explain. Prisutan je u gotovo svim najčešćim PostgreSQL distribucijama i može se jednostavno aktivirati u konfiguracijskoj datoteci.

Ako shvati da neki zahtjev traje duže od ograničenja koje ste mu rekli, onda to radi “snimka” plana ovog zahtjeva i zajedno ih upisuje u dnevnik.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Čini se da je sada sve u redu, idemo do klade i vidimo tamo... [tekst krpa za noge]. Ali ne možemo ništa reći o tome, osim činjenice da je to odličan plan jer je bilo potrebno 11 ms da se izvrši.

Čini se da je sve u redu - ali ništa nije jasno šta se zapravo dogodilo. Osim opšteg vremena, mi zapravo ne vidimo ništa. Jer gledanje u takvo "jagnje" običnog teksta općenito nije vizualno.

Ali čak i ako nije očigledno, čak i ako je nezgodno, postoje fundamentalniji problemi:

  • Čvor pokazuje zbir resursa cijelog podstabla ispod njega. Odnosno, ne možete samo saznati koliko je vremena potrošeno na ovo skeniranje indeksa ako postoji neki ugniježđeni uvjet ispod njega. Moramo dinamički pogledati da li postoje „djeca“ i uslovne varijable, CTE-ovi unutra – i oduzeti sve ovo „u našim umovima“.
  • Druga tačka: vrijeme koje je naznačeno na čvoru je vrijeme izvršenja jednog čvora. Ako je ovaj čvor izvršen kao rezultat, na primjer, petlje kroz zapise tablice nekoliko puta, tada se broj petlji - ciklusa ovog čvora - povećava u planu. Ali samo atomsko vrijeme izvršenja ostaje isto u smislu plana. Odnosno, da biste razumjeli koliko je ovaj čvor ukupno izveden, morate pomnožiti jednu stvar s drugom - opet, "u svojoj glavi".

U takvim situacijama shvatite „Ko je najslabija karika?“ skoro nemoguće. Stoga, čak i sami programeri to pišu u "priručniku". “Razumijevanje plana je umjetnost koja se mora naučiti, doživjeti...”.

Ali imamo 1000 programera i ne možete prenijeti ovo iskustvo svakom od njih. Ja, ti, on zna, ali neko tamo više ne zna. Možda će naučiti, a možda i ne, ali sada treba raditi - a gdje bi stekao ovo iskustvo?

Vizualizacija plana

Stoga smo shvatili da nam je za rješavanje ovih problema potrebno dobra vizualizacija plana. [članak]

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Prvo smo prošli “kroz tržište” – hajde da pogledamo na internetu da vidimo šta uopšte postoji.

Ali pokazalo se da postoji vrlo malo relativno “živih” rješenja koja se manje-više razvijaju – doslovno, samo jedno: objasni.depesz.com autora Huberta Lubaczewskog. Kada unesete u polje “feed” tekstualni prikaz plana, on vam pokazuje tabelu sa raščlanjenim podacima:

  • vlastito vrijeme obrade čvora
  • ukupno vrijeme za cijelo podstablo
  • broj preuzetih zapisa koji su statistički očekivani
  • samog tijela čvora

Ovaj servis također ima mogućnost dijeljenja arhive linkova. Ubacio si svoj plan i rekao: "Hej, Vasja, evo linka, tu nešto nije u redu."

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Ali postoje i mali problemi.

Prvo, ogromna količina "copy-paste". Uzmete komad trupca, zabijete ga unutra, i opet, i ponovo.

Drugo, nema analize količine pročitanih podataka — isti baferi koji izlaze EXPLAIN (ANALYZE, BUFFERS), mi to ovdje ne vidimo. On jednostavno ne zna kako ih rastaviti, razumjeti i raditi s njima. Kada čitate puno podataka i shvatite da možda pogrešno dodjeljujete keš diska i memorije, ove informacije su vrlo važne.

Treća negativna tačka je veoma slab razvoj ovog projekta. Urezivanja su jako mala, dobro je jednom u šest mjeseci, a kod je u Perlu.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Ali ovo je sve "stihovi", mogli bismo nekako da živimo sa ovim, ali ima jedna stvar koja nas je jako odvratila od ove usluge. To su greške u analizi Common Table Expression (CTE) i raznim dinamičkim čvorovima poput InitPlan/SubPlan.

Ako vjerujete ovoj slici, onda je ukupno vrijeme izvršenja svakog pojedinačnog čvora veće od ukupnog vremena izvršenja cijelog zahtjeva. jednostavno je - vrijeme generiranja ovog CTE nije oduzeto od čvora CTE skeniranja. Stoga više ne znamo tačan odgovor na to koliko je trajalo samo CTE skeniranje.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Tada smo shvatili da je vrijeme da napišemo svoje - ura! Svaki programer kaže: "Sada ćemo napisati svoje, biće super lako!"

Uzeli smo stack tipičan za web servise: jezgro bazirano na Node.js + Express, koristili Bootstrap i D3.js za prekrasne dijagrame. I naša očekivanja su se u potpunosti opravdala - prvi prototip smo dobili za 2 sedmice:

  • prilagođeni analizator plana
    To jest, sada možemo raščlaniti bilo koji plan od onih koje je generirao PostgreSQL.
  • ispravna analiza dinamičkih čvorova - CTE skeniranje, InitPlan, SubPlan
  • analiza distribucije bafera - gdje se stranice s podacima čitaju iz memorije, gdje iz lokalne keš memorije, gdje s diska
  • dobio jasnoću
    Da ne bi sve ovo „kopali“ po logu, već da bismo odmah na slici videli „najslabu kariku“.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Dobili smo nešto poput ovoga, sa uključenim isticanjem sintakse. Ali obično naši programeri više ne rade sa potpunim prikazom plana, već sa kraćim. Uostalom, sve smo brojeve već raščlanili i bacili lijevo i desno, a u sredini smo ostavili samo prvi red, kakav je to čvor: CTE Scan, CTE generacija ili Seq Scan prema nekom znaku.

Ovo je skraćeni prikaz koji zovemo predložak plana.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Šta bi drugo bilo zgodno? Bilo bi zgodno vidjeti koliki je dio našeg ukupnog vremena dodijeljen kojem čvoru - i samo ga "zalijepiti" sa strane pita dijagram.

Pokazujemo na čvor i vidimo - ispostavilo se da je Seq Scan zauzeo manje od četvrtine ukupnog vremena, a preostale 3/4 je preuzeo CTE Scan. Užas! Ovo je mala napomena o "brzi paljbe" CTE skeniranja ako ih aktivno koristite u svojim upitima. Nisu baš brzi - inferiorni su čak i od običnog skeniranja stola. [članak] [članak]

Ali obično su takvi dijagrami zanimljiviji, složeniji, kada odmah pokažemo na segment i vidimo, na primjer, da je više od polovine vremena neki Seq Scan “pojeo”. Štaviše, unutra je bio nekakav Filter, dosta zapisa je odbačeno prema njemu... Možete direktno baciti ovu sliku programeru i reći: „Vasya, ovdje je sve loše za tebe! Shvati, vidi – nešto nije u redu!”

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Naravno, tu su bile i neke "grablje".

Prvo na šta smo naišli je problem zaokruživanja. Vrijeme svakog pojedinog čvora u planu je naznačeno s tačnošću od 1 μs. A kada broj ciklusa čvora pređe, na primjer, 1000 - nakon izvršenja PostgreSQL-a podijeljeno "unutar preciznosti", tada pri izračunavanju nazad dobijamo ukupno vrijeme "negdje između 0.95 ms i 1.05 ms". Kada brojanje krene u mikrosekunde, to je u redu, ali kada je već [mili]sekunde, morate uzeti u obzir ovu informaciju kada „odvezujete“ resurse na čvorove plana „ko je koliko potrošio“.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Druga tačka, složenija, je distribucija resursa (onih bafera) među dinamičkim čvorovima. To nas je koštalo prve 2 sedmice prototipa plus još 4 sedmice.

Prilično je lako doći do ovakvog problema - radimo CTE i navodno nešto čitamo u njemu. U stvari, PostgreSQL je “pametan” i neće čitati ništa direktno tamo. Zatim uzimamo prvi zapis iz njega, a njemu sto prvi zapis iz istog CTE-a.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Gledamo plan i razumijemo - čudno je, imamo 3 bafera (stranice sa podacima) "potrošena" u Seq Scan, još 1 u CTE skeniranju i još 2 u drugom CTE skeniranju. Odnosno, ako jednostavno sve zbrojimo, dobićemo 6, ali sa tableta čitamo samo 3! CTE skeniranje ne čita ništa nigdje, već radi direktno s procesnom memorijom. Odnosno, ovde nešto očigledno nije u redu!

Zapravo, ispada da su ovdje sve one 3 stranice podataka koje su tražene od Seq Scan-a, prvo je 1 tražila 1. CTE skeniranje, a zatim 2., pa su mu pročitane još 2. To jest, ukupno 3 stranice su pročitani podaci, a ne 6.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

I ova slika nas je dovela do shvaćanja da izvršenje plana više nije stablo, već jednostavno neka vrsta acikličkog grafa. I dobili smo ovakav dijagram, tako da razumemo „šta je uopšte došlo odakle“. Odnosno, ovdje smo kreirali CTE iz pg_class, i tražili ga dva puta, i skoro svo naše vrijeme smo potrošili na granu kada smo ga tražili drugi put. Jasno je da je čitanje 2. unosa mnogo skuplje nego samo čitanje 101. unosa sa tableta.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Izdisali smo neko vrijeme. Rekli su: „Sada, Neo, znaš kung fu! Sada je naše iskustvo na vašem ekranu. Sada ga možete koristiti." [članak]

Konsolidacija dnevnika

Naših 1000 programera je odahnulo. Ali shvatili smo da imamo samo stotine "borbenih" servera, a sve ovo "copy-paste" od strane programera nije nimalo zgodno. Shvatili smo da to moramo sami prikupiti.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Općenito, postoji standardni modul koji može prikupljati statistiku, međutim, on također mora biti aktiviran u konfiguraciji - ovo modul pg_stat_statements. Ali on nam nije odgovarao.

Prvo, dodeljuje istim upitima koristeći različite šeme unutar iste baze podataka različiti QueryIds. Odnosno, ako to prvo uradite SET search_path = '01'; SELECT * FROM user LIMIT 1;a onda SET search_path = '02'; i isti zahtjev, onda će statistika ovog modula imati različite evidencije, i neću moći prikupljati opštu statistiku posebno u kontekstu ovog profila zahtjeva, bez uzimanja u obzir šema.

Druga stvar koja nas je spriječila da ga koristimo je nedostatak planova. Odnosno, nema plana, postoji samo sam zahtjev. Vidimo šta se usporava, ali ne razumemo zašto. I ovdje se vraćamo na problem skupa podataka koji se brzo mijenja.

I poslednji trenutak - nedostatak "činjenica". Odnosno, ne možete adresirati specifičnu instancu izvršenja upita - ne postoji, postoji samo agregirana statistika. Iako je moguće raditi s ovim, to je jednostavno vrlo teško.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Stoga smo se odlučili boriti protiv copy-paste-a i počeli pisati kolektor.

Kolektor se povezuje preko SSH-a, uspostavlja sigurnu vezu sa serverom sa bazom podataka koristeći sertifikat i tail -F "prianja" za njega u log fajlu. Dakle, u ovoj sesiji dobijamo kompletno „ogledalo“ čitavog log fajla, koje server generiše. Opterećenje samog servera je minimalno, jer tamo ništa ne analiziramo, samo ogledamo promet.

Pošto smo već počeli da pišemo interfejs u Node.js, nastavili smo sa pisanjem kolektora u njemu. I ova tehnologija se opravdala, jer je vrlo zgodno koristiti JavaScript za rad sa slabo formatiranim tekstualnim podacima, a to je dnevnik. I sama Node.js infrastruktura kao pozadinska platforma omogućava vam lak i praktičan rad sa mrežnim konekcijama, i zapravo sa bilo kojim tokovima podataka.

U skladu s tim, "razvlačimo" dvije veze: prvu da "slušamo" sam dnevnik i uzmemo ga sebi, a drugu da povremeno pitamo bazu. „Ali dnevnik pokazuje da je znak sa oid 123 blokiran“, ali to ne znači ništa za programera, i bilo bi lijepo pitati bazu podataka: „Šta je uopće OID = 123?“ I tako periodično pitamo bazu šta još ne znamo o sebi.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

„Postoji samo jedna stvar koju niste uzeli u obzir, postoji vrsta pčela nalik na slonove!..” Počeli smo da razvijamo ovaj sistem kada smo želeli da nadgledamo 10 servera. Najkritičniji u našem razumijevanju, gdje su se pojavili neki problemi s kojima je bilo teško nositi se. Ali tokom prvog kvartala dobili smo stotinu za praćenje - jer sistem je funkcionisao, svi su to želeli, svima je bilo udobno.

Sve ovo treba zbrojiti, protok podataka je velik i aktivan. U stvari, ono što pratimo, ono sa čime se možemo nositi je ono što koristimo. Takođe koristimo PostgreSQL kao skladište podataka. I ništa nije brže za "ulijevanje" podataka u njega od operatera COPY Ne još.

Ali jednostavno „prebacivanje“ podataka zapravo nije naša tehnologija. Jer ako imate otprilike 50 zahtjeva u sekundi na sto servera, onda će to generirati 100-150 GB dnevnika dnevno. Zbog toga smo morali pažljivo "izrezati" bazu.

Prvo, jesmo podjela po danu, jer, uglavnom, nikoga ne zanima korelacija između dana. Kakve veze ima ono što ste imali jučer, ako ste večeras izbacili novu verziju aplikacije - i već neke nove statistike.

Drugo, naučili smo (bili smo prisiljeni) vrlo, vrlo brzo za pisanje COPY. Odnosno, ne samo COPYjer je brži od INSERT, pa čak i brže.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Treća tačka - morao sam napuštanje okidača, odnosno stranih ključeva. Odnosno, mi uopšte nemamo referentni integritet. Jer ako imate tabelu koja ima par FK-ova, a u strukturi baze podataka kažete da „ovo je zapis dnevnika koji je referenciran od strane FK, na primjer, na grupu zapisa“, onda kada ga ubacite, PostgreSQL ne preostaje ništa osim kako to uzeti i učiniti pošteno SELECT 1 FROM master_fk1_table WHERE ... sa identifikatorom koji pokušavate da ubacite - samo da proverite da li je ovaj zapis prisutan tamo, da ne "prekidate" ovaj strani ključ svojim umetanjem.

Umjesto jednog zapisa u ciljnoj tabeli i njenim indeksima, dobijamo dodatnu korist čitanja iz svih tabela na koje se odnosi. Ali to nam uopće ne treba - naš zadatak je snimiti što je više moguće i što je brže moguće uz najmanje opterećenje. Pa FK - dole!

Sljedeća tačka je agregacija i heširanje. U početku smo ih implementirali u bazu podataka - uostalom, zgodno je odmah, kada stigne zapis, to učiniti na nekoj vrsti tableta "plus jedan" pravo na okidaču. Pa, zgodno je, ali ista loša stvar - ubacite jedan zapis, ali ste prisiljeni čitati i pisati nešto drugo iz druge tabele. Štaviše, ne samo da čitate i pišete, već to radite svaki put.

Sada zamislite da imate tabelu u kojoj jednostavno brojite broj zahtjeva koji su prošli kroz određeni host: +1, +1, +1, ..., +1. A vama, u principu, ovo ne treba - sve je moguće suma u memoriji na kolektoru i poslati u bazu podataka u jednom potezu +10.

Da, u slučaju nekih problema, vaš logički integritet može se “raspasti”, ali ovo je gotovo nerealan slučaj – jer imate normalan server, ima bateriju u kontroleru, imate dnevnik transakcija, log na sistem datoteka... Generalno, ne vredi. Gubitak produktivnosti koji dobijete pokretanjem okidača/FK nije vrijedan troškova koje imate.

Isto je i sa heširanjem. Doleti vam određeni zahtjev, vi iz njega izračunate određeni identifikator u bazi podataka, upišete ga u bazu i onda to kažete svima. Sve je u redu dok vam u trenutku snimanja ne dođe druga osoba koja želi da snimi isto - i budete blokirani, a ovo je već loše. Stoga, ako možete prenijeti generiranje nekih ID-ova na klijenta (u odnosu na bazu podataka), bolje je to učiniti.

Savršeno nam je bilo da koristimo MD5 iz teksta - zahtjev, plan, šablon,... Računamo ga na strani kolektora, a gotov ID "sipamo" u bazu podataka. Dužina MD5 i dnevno particioniranje omogućavaju nam da ne brinemo o mogućim kolizijama.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Ali da bismo sve ovo brzo snimili, morali smo izmijeniti samu proceduru snimanja.

Kako obično pišete podatke? Imamo neku vrstu skupa podataka, podijelimo ga u nekoliko tabela, pa ga KOPIRAJEM - prvo u prvu, pa u drugu, u treću... Nezgodno je, jer izgleda da pišemo jedan tok podataka u tri koraka sekvencijalno. Neprijatno. Može li se to brže uraditi? Može!

Da biste to učinili, dovoljno je samo razložiti ove tokove paralelno jedan s drugim. Ispostavilo se da imamo greške, zahtjeve, šablone, blokade, ... lete u odvojenim nitima - i sve to pišemo paralelno. Dovoljno za ovo držati COPY kanal stalno otvoren za svaku pojedinačnu ciljnu tablicu.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Odnosno kod kolektora uvek postoji potok, u koji mogu upisati podatke koji su mi potrebni. Ali da baza vidi ove podatke, a da neko ne zaglavi čekajući da se ti podaci zapišu, COPY mora biti prekinut u određenim intervalima. Za nas je najefikasniji period bio oko 100ms - zatvaramo ga i odmah otvaramo ponovo za isti sto. A ako nemamo dovoljno jednog protoka tokom nekih pikova, onda radimo udruživanje do određene granice.

Dodatno, otkrili smo da je za takav profil opterećenja svako združivanje, kada se zapisi prikupljaju u paketima, zlo. Klasično zlo je INSERT ... VALUES i još 1000 zapisa. Jer u tom trenutku imate vrhunac pisanja na mediju, a svi ostali koji pokušavaju nešto zapisati na disk će čekati.

Da biste se riješili takvih anomalija, jednostavno nemojte ništa agregirati, ne baferuj uopšte. A ako dođe do baferovanja na disk (na sreću, Stream API u Node.js vam omogućava da saznate) - odgodite ovu vezu. Kada primite događaj da je ponovo slobodan, pišite mu iz akumuliranog reda čekanja. I dok je zauzeto, uzmite sljedeći besplatni iz bazena i pišite mu.

Prije uvođenja ovakvog pristupa snimanju podataka imali smo otprilike 4K operacija pisanja i na taj način smo smanjili opterećenje za 4 puta. Sada su narasli još 6 puta zbog novih praćenih baza podataka - do 100MB/s. A sada pohranjujemo dnevnike za posljednja 3 mjeseca u zapremini od oko 10-15TB, nadajući se da će za samo tri mjeseca bilo koji programer moći riješiti bilo koji problem.

Razumijemo probleme

Ali jednostavno prikupljanje svih ovih podataka je dobro, korisno, relevantno, ali nije dovoljno – to treba razumjeti. Jer ovo su milioni različitih planova dnevno.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Ali milioni su neupravljivi, prvo moramo učiniti „manje“. I, prije svega, morate odlučiti kako ćete organizirati ovu „manju“ stvar.

Identifikovali smo tri ključne tačke:

  • ko poslao ovaj zahtjev
    Odnosno, iz koje aplikacije je "stigao": web interfejs, backend, sistem plaćanja ili nešto drugo.
  • gdje desilo se
    Na kom konkretnom serveru? Jer ako imate više servera pod jednom aplikacijom, a odjednom jedan "poglupi" (jer je "disk pokvaren", "procurila memorija", neki drugi problem), onda se morate posebno obratiti serveru.
  • kako problem se manifestovao na ovaj ili onaj način

Da bismo razumjeli "ko" nam je poslao zahtjev, koristimo standardni alat - postavljanje varijable sesije: SET application_name = '{bl-host}:{bl-method}'; — šaljemo ime hosta poslovne logike sa kojeg dolazi zahtjev i naziv metode ili aplikacije koja ga je pokrenula.

Nakon što smo proslijedili “vlasnika” zahtjeva, on se mora ispisati u dnevnik - za to konfiguriramo varijablu log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Za zainteresovane, možda pogledajte u priručnikušta sve to znači. Ispada da u dnevniku vidimo:

  • время
  • identifikatori procesa i transakcija
  • ime baze podataka
  • IP osobe koja je poslala ovaj zahtjev
  • i naziv metode

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Tada smo shvatili da nije baš zanimljivo gledati korelaciju za jedan zahtjev između različitih servera. Nije često da imate situaciju da jedna aplikacija jednako zezne tu i tamo. Ali čak i ako je isto, pogledajte bilo koji od ovih servera.

Dakle, ovo je rez "jedan server - jedan dan" pokazalo se da nam je to bilo dovoljno za bilo kakvu analizu.

Prvi analitički dio je isti "uzorak" - skraćeni oblik prikaza plana, očišćen od svih brojčanih indikatora. Drugi rez je aplikacija ili metoda, a treći rez je specifični čvor plana koji nam je uzrokovao probleme.

Kada smo prešli sa određenih instanci na šablone, dobili smo dve prednosti odjednom:

  • višestruko smanjenje broja objekata za analizu
    Problem više ne moramo analizirati hiljadama upita ili planova, već desetinama šablona.
  • vremenska linija
    Odnosno, sažimanjem „činjenica“ unutar određenog odeljka, možete prikazati njihov izgled tokom dana. I ovdje možete razumjeti da ako imate neki obrazac koji se dešava, na primjer, jednom na sat, ali bi se trebao dogoditi jednom dnevno, trebali biste razmisliti šta je pošlo po zlu - ko je to izazvao i zašto, možda bi trebao biti ovdje ne bi trebalo. Ovo je još jedna nenumerička, čisto vizuelna, metoda analize.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Preostale metode su bazirane na pokazateljima koje izdvajamo iz plana: koliko puta se takav obrazac dogodio, ukupno i prosječno vrijeme, koliko je podataka pročitano s diska, a koliko iz memorije...

Jer, na primjer, dođete na stranicu analitike za host, vidite - nešto počinje previše čitati na disku. Disk na serveru to ne može podnijeti - ko čita sa njega?

A možete sortirati po bilo kojoj koloni i odlučiti čime ćete se trenutno baviti - opterećenjem procesora ili diska, ili ukupnim brojem zahtjeva... Mi smo to sortirali, pogledali one "top", popravili i predstavila novu verziju aplikacije.
[video predavanje]

I odmah možete vidjeti različite aplikacije koje dolaze s istim šablonom iz zahtjeva poput SELECT * FROM users WHERE login = 'Vasya'. Frontend, backend, obrada... I pitate se zašto bi obrada čitala korisnika ako on nije u interakciji s njim.

Suprotan način je da odmah vidite iz aplikacije šta radi. Na primjer, frontend je ovo, ovo, ovo i ovo jednom na sat (vremenska linija pomaže). I odmah se postavlja pitanje: izgleda da nije posao frontenda da radi nešto jednom na sat...

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Nakon nekog vremena, shvatili smo da nam nedostaje agregacija statistika po čvorovima plana. Iz planova smo izolovali samo one čvorove koji rade nešto sa podacima samih tabela (čitaju/upisuju ih po indeksu ili ne). Zapravo, samo je jedan aspekt dodat u odnosu na prethodnu sliku - koliko nam je zapisa donio ovaj čvor?i koliko ih je odbačeno (Redovi su uklonjeni filterom).

Nemate odgovarajući indeks na tablici, zatražite mu, on proleti pored indeksa, upadne u Seq Scan... filtrirali ste sve zapise osim jednog. Zašto vam je potrebno 100 miliona filtriranih zapisa dnevno? Nije li bolje da se indeks savije?

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

Analizirajući sve planove čvor po čvor, shvatili smo da u planovima postoje neke tipične strukture koje će vrlo vjerovatno izgledati sumnjivo. I bilo bi lijepo reći programeru: "Prijatelju, ovdje prvo čitaš po indeksu, zatim sortiraj, a zatim odsijeci" - u pravilu postoji jedan zapis.

Svi koji su pisali upite vjerovatno su se susreli sa ovim obrascem: „Daj mi zadnju narudžbu za Vasju, njen datum.” A ako nemate indeks po datumu, ili nema datuma u indeksu koji ste koristili, onda ćete stane na potpuno iste "grablje".

Ali znamo da je ovo „grablje“ - pa zašto ne biste odmah rekli programeru šta treba da uradi. Shodno tome, kada sada otvori plan, naš programer odmah vidi prelijepu sliku sa savjetima, gdje mu odmah kažu: „Imaš problema tu i tamo, ali oni se rješavaju ovako i onako“.

Kao rezultat toga, količina iskustva koja je bila potrebna za rješavanje problema na početku, a sada je značajno opala. Ovo je vrsta alata koji imamo.

Masovna optimizacija PostgreSQL upita. Kiril Borovikov (Tenzor)

izvor: www.habr.com

Dodajte komentar