Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Ataskaitoje pateikiami keli metodai, leidžiantys stebėti SQL užklausų našumą, kai jų yra milijonai per dieną, ir yra šimtai stebimų PostgreSQL serverių.

Kokie techniniai sprendimai leidžia efektyviai apdoroti tokį informacijos kiekį ir kaip tai palengvina eilinio kūrėjo gyvenimą?


Kas domisi? konkrečių problemų analizė ir įvairūs optimizavimo būdai SQL užklausos ir tipiškų DBA problemų sprendimas PostgreSQL – taip pat galite skaityti straipsnių seriją šia tema.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)
Mano vardas Kirilas Borovikovas, aš atstovauju Tensor kompanija. Konkrečiai, aš specializuojasi dirbant su duomenų bazėmis mūsų įmonėje.

Šiandien papasakosiu, kaip optimizuojame užklausas, kai nereikia „atrinkti“ vienos užklausos našumo, o spręsti problemą masiškai. Kai užklausų yra milijonai, o kai kurias reikia rasti sprendimo būdai šią didelę problemą.

Apskritai Tensor milijonui mūsų klientų yra VLSI yra mūsų programa: įmonių socialinis tinklas, sprendimai video komunikacijai, vidiniam ir išoriniam dokumentų srautui, apskaitos sistemos buhalterinei apskaitai ir sandėliams,... Tai yra toks integruoto verslo valdymo „megakombainas“, kuriame daugiau nei 100 skirtingų vidaus projektai.

Siekdami užtikrinti, kad jie visi veiktų ir vystytųsi normaliai, turime 10 plėtros centrų visoje šalyje, juose yra daugiau 1000 kūrėjų.

Su PostgreSQL dirbame nuo 2008 m. ir sukaupėme daug to, ką apdorojame – klientų duomenis, statistinius, analitinius, duomenis iš išorinių informacinių sistemų – daugiau nei 400 TB. Vien gamyboje yra apie 250 serverių, o iš viso yra apie 1000 mūsų stebimų duomenų bazių serverių.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

SQL yra deklaratyvi kalba. Jūs aprašote ne „kaip“ kažkas turėtų veikti, o „ką“ norite pasiekti. DBVS geriau žino, kaip prisijungti – kaip sujungti lenteles, kokias sąlygas nustatyti, kas bus per indeksą, o kas ne...

Kai kurios DBVS priima patarimus: „Ne, sujunkite šias dvi lenteles tokioje ir tokioje eilėje“, tačiau PostgreSQL to padaryti negali. Tai yra sąmoninga pirmaujančių kūrėjų pozicija: „Geriau baigsime užklausų optimizavimo priemonę, nei leisime kūrėjams naudoti kažkokias užuominas“.

Tačiau nepaisant to, kad PostgreSQL neleidžia „išorei“ savęs valdyti, tai puikiai leidžia pamatyti, kas vyksta jo vidujekai vykdote užklausą ir kur kyla problemų.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Apskritai, su kokiomis klasikinėmis problemomis dažniausiai susiduria kūrėjas [į DBA]? „Čia įvykdėme prašymą, ir pas mus viskas lėtai, viskas kabo, kažkas vyksta... Kažkokia bėda!“

Priežastys beveik visada yra tos pačios:

  • neefektyvus užklausos algoritmas
    Kūrėjas: "Dabar aš duodu jam 10 lentelių SQL per JOIN..." - ir tikisi, kad jo sąlygos stebuklingai bus "atsiejamos" ir jis viską gaus greitai. Bet stebuklų nebūna, ir bet kuri sistema su tokiu kintamumu (10 lentelių viename FROM) visada duoda kažkokią klaidą. [straipsnis]
  • nereikšminga statistika
    Šis punktas labai aktualus būtent PostgreSQL, kai „įpylėte“ didelį duomenų rinkinį į serverį, pateikiate užklausą ir jis „sekskanizuoja“ jūsų planšetinį kompiuterį. Nes vakar jame buvo 10 įrašų, o šiandien – 10 milijonų, bet PostgreSQL dar to nežino, ir mes turime apie tai pasakyti. [straipsnis]
  • „prijungti“ išteklius
    Įdiegėte didelę ir labai apkrautą duomenų bazę silpname serveryje, kuriame nėra pakankamai disko, atminties ar procesoriaus našumo. Ir viskas... Kai kur yra spektaklio lubos, virš kurių nebegalima peršokti.
  • blokavimas
    Tai sunkus dalykas, tačiau jie aktualiausi įvairioms modifikuojančioms užklausoms (INSERT, UPDATE, DELETE) – tai atskira didelė tema.

Plano gavimas

...Ir dėl viso kito mes reikia plano! Turime pamatyti, kas vyksta serveryje.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

„PostgreSQL“ užklausos vykdymo planas yra užklausos vykdymo algoritmo tekstas. Būtent algoritmas, planuotojo atliktos analizės rezultatas, buvo veiksmingiausias.

Kiekvienas medžio mazgas yra operacija: duomenų gavimas iš lentelės arba rodyklės, bitų schemos kūrimas, dviejų lentelių sujungimas, sujungimas, susikirtimas arba pasirinkimų neįtraukimas. Vykdant užklausą reikia eiti per šio medžio mazgus.

Norėdami gauti užklausos planą, paprasčiausias būdas yra įvykdyti teiginį EXPLAIN. Norėdami gauti visus tikrus atributus, ty iš tikrųjų vykdyti užklausą bazėje - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Blogoji dalis: kai paleidžiate, tai atsitinka „čia ir dabar“, todėl tinka tik vietiniam derinimui. Jei pasirinksite labai apkrautą serverį, kuriame vyksta dideli duomenų pasikeitimai, ir pamatysite: „O! Čia mes turime lėtą vykdymąXia prašymas“. Prieš pusvalandį, prieš valandą – kol vykdėte ir gavote šią užklausą iš žurnalų, grąžindami ją į serverį, pasikeitė visas duomenų rinkinys ir statistika. Paleidžiate, kad derintumėte – ir jis veikia greitai! Ir tu negali suprasti, kodėl, kodėl ji buvo lėtai.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Siekdami suprasti, kas nutiko būtent tuo momentu, kai užklausa buvo įvykdyta serveryje, parašė protingi žmonės auto_explain modulis. Jis yra beveik visuose labiausiai paplitusiuose PostgreSQL paskirstymuose ir gali būti tiesiog suaktyvintas konfigūracijos faile.

Jei ji supranta, kad kuri nors užklausa vykdoma ilgiau nei nurodyta riba, ji tai daro šio prašymo plano „momentinę nuotrauką“ ir surašo juos į žurnalą.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Dabar lyg ir viskas gerai, einam prie rąsto ir ten pažiūrim... [tekstas kojytės]. Tačiau apie tai nieko negalime pasakyti, išskyrus tai, kad tai puikus planas, nes jo vykdymas užtruko 11 ms.

Atrodo, kad viskas gerai, bet niekas neaišku, kas iš tikrųjų atsitiko. Be bendro laiko, mes tikrai nieko nematome. Nes žiūrėti į tokį paprasto teksto „ėriuką“ paprastai nėra vizualiai.

Bet net jei tai nėra akivaizdu, net jei tai nepatogu, yra ir daugiau esminių problemų:

  • Mazgas rodo viso pomedžio išteklių suma po juo. Tai yra, jūs negalite tiesiog sužinoti, kiek laiko buvo praleista šiam konkrečiam indekso nuskaitymui, jei po juo yra kokia nors įdėta sąlyga. Turime dinamiškai žiūrėti, ar viduje yra „vaikų“ ir sąlyginių kintamųjų, CTE, ir visa tai atimti „mūsų mintyse“.
  • Antras punktas: laikas, nurodytas mazge vieno mazgo vykdymo laikas. Jei šis mazgas buvo vykdomas, pavyzdžiui, kelis kartus persukant lentelės įrašus, plano kilpų – šio mazgo ciklų – skaičius didėja. Tačiau pats atominis vykdymo laikas plano požiūriu išlieka toks pat. Tai yra, norint suprasti, kiek laiko iš viso buvo atliktas šis mazgas, turite padauginti vieną dalyką iš kito - vėlgi, „galvoje“.

Tokiose situacijose supraskite „Kas yra silpniausia grandis? beveik neįmanoma. Todėl net patys kūrėjai rašo „vadovėje“, kad „Plano supratimas yra menas, kurio reikia išmokti, patirti...“.

Tačiau turime 1000 kūrėjų, ir jūs negalite perteikti šios patirties kiekvienam iš jų. Aš, tu, jis žinau, bet kažkas ten nebežino. Gal išmoks, o gal ir ne, bet dirbti reikia dabar – o iš kur jis pasisemtų šios patirties?

Plano vizualizacija

Todėl supratome, kad norint susidoroti su šiomis problemomis, mums reikia gera plano vizualizacija. [straipsnis]

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Pirmiausia nuėjome „per rinką“ – pažiūrėkime internete, kas išvis egzistuoja.

Tačiau paaiškėjo, kad yra labai mažai santykinai „gyvų“ sprendimų, kurie daugiau ar mažiau tobulinami - tiesiogine prasme, tik vienas: paaiškinkite.depesz.com Hubertas Lubačevskis. Kai įvesite į lauką „tiekimo kanalas“ plano tekstinį vaizdą, bus rodoma lentelė su išanalizuotais duomenimis:

  • paties mazgo apdorojimo laikas
  • bendras viso pomedžio laikas
  • gautų įrašų, kurių statistiškai tikėtasi, skaičius
  • pats mazgo kūnas

Ši paslauga taip pat turi galimybę bendrinti nuorodų archyvą. Įmetėte savo planą ir pasakėte: „Ei, Vasya, čia yra nuoroda, ten kažkas negerai“.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Tačiau yra ir nedidelių problemų.

Pirma, didžiulis „copy-paste“ kiekis. Paimi rąsto gabalą, įkišai jį ten, ir vėl, ir vėl.

Antra, neanalizuojamas nuskaitytų duomenų kiekis - tie patys buferiai, kurie išveda EXPLAIN (ANALYZE, BUFFERS), mes jo čia nematome. Jis tiesiog nemoka jų išardyti, suprasti ir dirbti su jais. Kai skaitote daug duomenų ir suprantate, kad galite netinkamai paskirstyti diską ir atminties talpyklą, ši informacija yra labai svarbi.

Trečias neigiamas momentas – labai silpna šio projekto plėtra. Įsipareigojimai yra labai maži, gerai, jei kartą per šešis mėnesius, o kodas yra Perl.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Bet visa tai yra „lyrika“, galėtume kažkaip su tuo gyventi, tačiau yra vienas dalykas, kuris mus labai atitolino nuo šios paslaugos. Tai yra bendrosios lentelės išraiškos (CTE) ir įvairių dinaminių mazgų, tokių kaip „InitPlan“ / „SubPlan“, analizės klaidos.

Jei tikite šiuo paveikslu, tada bendras kiekvieno atskiro mazgo vykdymo laikas yra didesnis nei bendras visos užklausos vykdymo laikas. Tai paprasta - šio CTE generavimo laikas nebuvo atimtas iš CTE Scan mazgo. Todėl nebežinome teisingo atsakymo, kiek laiko užtruko pats CTE skenavimas.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Tada supratome, kad atėjo laikas rašyti savo – hurra! Kiekvienas kūrėjas sako: „Dabar mes rašysime savo, tai bus labai paprasta!

Mes pasirinkome žiniatinklio paslaugoms būdingą krūvą: branduolį, pagrįstą Node.js + Express, naudojome Bootstrap ir D3.js, kad padarytumėte gražias diagramas. Ir mūsų lūkesčiai pasiteisino – pirmąjį prototipą gavome per 2 savaites:

  • pasirinktinio plano analizatorius
    Tai reiškia, kad dabar galime išanalizuoti bet kurį planą iš tų, kuriuos sugeneravo PostgreSQL.
  • teisinga dinaminių mazgų analizė - CTE nuskaitymas, „InitPlan“, „SubPlan“.
  • buferių pasiskirstymo analizė - kur duomenų puslapiai skaitomi iš atminties, kur iš vietinės talpyklos, kur iš disko
  • gavo aiškumo
    Kad viso to „neiškasti“ žurnale, o paveikslėlyje iš karto matyti „silpniausia grandis“.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Gavome kažką panašaus su sintaksės paryškinimu. Tačiau dažniausiai mūsų kūrėjai dirba nebe su pilnu plano atvaizdavimu, o su trumpesniu. Juk jau visus skaičius išanalizuojame ir mėtėme į kairę ir į dešinę, o viduryje palikome tik pirmą eilutę, koks tai mazgas: CTE Scan, CTE generavimas ar Seq Scan pagal kažkokį ženklą.

Tai yra sutrumpintas vaizdavimas, kurį mes vadiname plano šablonas.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Kas dar būtų patogu? Būtų patogu pamatyti, kokia mūsų bendro laiko dalis kuriam mazgui skirta – ir tiesiog „priklijuoti“ prie šono pyragų diagrama.

Rodome į mazgą ir matome – pasirodo, Seq Scan užtruko mažiau nei ketvirtadalį viso laiko, o likusius 3/4 užėmė CTE Scan. Siaubas! Tai nedidelė pastaba apie CTE Scan „uždegimo greitį“, jei aktyviai naudojate juos savo užklausose. Jie nėra labai greiti – nusileidžia net įprastam lentelės skenavimui. [straipsnis] [straipsnis]

Tačiau dažniausiai tokios diagramos būna įdomesnės, sudėtingesnės, kai iškart nukreipiame į segmentą ir matome, pavyzdžiui, kad daugiau nei pusę laiko „suvalgo“ koks nors „Seq Scan“. Be to, viduje buvo kažkoks Filtras, pagal jį buvo išmesta daug įrašų... Galite tiesiai mesti šį paveikslėlį kūrėjui ir pasakyti: „Vasya, tau čia viskas blogai! Išsiaiškinkite, pažiūrėkite – kažkas negerai!

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Natūralu, kad buvo keletas „grėblių“.

Pirmas dalykas, su kuriuo susidūrėme, buvo apvalinimo problema. Kiekvieno atskiro mazgo laikas plane nurodomas 1 μs tikslumu. Ir kai mazgo ciklų skaičius viršija, pavyzdžiui, 1000 - po vykdymo PostgreSQL padalintas „tikslumo ribose“, tada skaičiuodami atgal gauname bendrą laiką „kažkur tarp 0.95 ms ir 1.05 ms“. Kai skaičiuojama iki mikrosekundžių, tai gerai, bet kai jau [mili]sekundės, turite atsižvelgti į šią informaciją, kai „atsiejate“ išteklius nuo plano „kas kiek suvartojo“ mazgų.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Antras dalykas, sudėtingesnis, yra išteklių (tų buferių) paskirstymas tarp dinaminių mazgų. Tai mums kainavo pirmas 2 prototipo savaites ir dar 4 savaites.

Gana lengva susidurti su tokia problema – atliekame CTE ir tariamai joje ką nors perskaitome. Tiesą sakant, „PostgreSQL“ yra „protingas“ ir nieko tiesiogiai ten neskaitys. Tada paimame iš jo pirmąjį įrašą, o į jį – šimtą pirmąjį iš tos pačios CTE.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Žiūrime į planą ir suprantame – keista, „Seq Scan“ „sunaudojome“ 3 buferius (duomenų puslapius), dar 1 CTE Scan ir dar 2 antrajame CTE Scan. Tai yra, jei viską susumasime, gausime 6, o iš planšetinio kompiuterio perskaitysime tik 3! CTE Scan nieko neskaito iš niekur, bet veikia tiesiogiai su proceso atmintimi. Tai yra, kažkas čia aiškiai negerai!

Tiesą sakant, išeina, kad čia yra visi tie 3 puslapiai duomenų, kurių buvo prašoma iš Seq Scan, iš pradžių 1 paprašė 1-ojo CTE nuskaitymo, o po to jam buvo perskaitytas antrasis ir dar 2. Tai yra iš viso Buvo perskaityti 2 puslapiai, o ne 3.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Ir šis paveikslas atvedė mus prie supratimo, kad plano vykdymas nebėra medis, o tiesiog kažkoks aciklinis grafikas. Ir gavome tokią diagramą, kad suprastume, „kas iš kur atsirado“. Tai yra, čia mes sukūrėme CTE iš pg_class ir paprašėme du kartus, ir beveik visas mūsų laikas buvo praleistas filiale, kai paprašėme jo 2 kartą. Akivaizdu, kad skaityti 101-ąjį įrašą yra daug brangiau nei tiesiog skaityti 1-ąjį įrašą iš planšetinio kompiuterio.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Kurį laiką iškvėpėme. Jie pasakė: „Dabar, Neo, tu žinai kung fu! Dabar mūsų patirtis rodoma jūsų ekrane. Dabar galite juo naudotis“. [straipsnis]

Žurnalų konsolidavimas

Mūsų 1000 kūrėjų lengviau atsiduso. Bet mes supratome, kad turime tik šimtus „kovinių“ serverių, o visa ši kūrėjų „copy-paste“ nėra visai patogu. Supratome, kad turime patys rinkti.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Apskritai yra standartinis modulis, galintis rinkti statistiką, tačiau jį taip pat reikia suaktyvinti konfigūracijose - tai modulis pg_stat_statements. Bet jis mums netiko.

Pirma, jis priskiria tas pačias užklausas, naudodamas skirtingas schemas toje pačioje duomenų bazėje skirtingi užklausos ID. Tai yra, jei pirmiausia tai padarysite SET search_path = '01'; SELECT * FROM user LIMIT 1;ir tada SET search_path = '02'; ir ta pati užklausa, tada šio modulio statistika turės skirtingus įrašus, ir aš negalėsiu rinkti bendrosios statistikos konkrečiai šio užklausos profilio kontekste, neatsižvelgdamas į schemas.

Antras dalykas, kuris neleido mums juo naudotis planų trūkumas. Tai yra, plano nėra, yra tik pats prašymas. Matome, kas sulėtėjo, bet nesuprantame kodėl. Ir čia grįžtame prie greitai besikeičiančio duomenų rinkinio problemos.

Ir paskutinė akimirka - "faktų" trūkumas. Tai reiškia, kad negalite kreiptis į konkretų užklausos vykdymo atvejį – jo nėra, yra tik apibendrinta statistika. Nors su tuo dirbti įmanoma, tai tiesiog labai sunku.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Todėl nusprendėme kovoti su copy-paste ir pradėjome rašyti kolektorius.

Kolektorius prisijungia per SSH, užmezga saugų ryšį su serveriu su duomenų baze naudodamas sertifikatą ir tail -F „prisikabina“ prie jo žurnalo faile. Taigi šioje sesijoje gauname pilną viso žurnalo failo „veidrodį“., kurį generuoja serveris. Pačio serverio apkrova minimali, nes ten nieko neparsuojame, tik atspindime srautą.

Kadangi sąsają jau pradėjome rašyti Node.js, toliau joje rašėme kolektorius. Ir ši technologija pasiteisino, nes labai patogu naudoti JavaScript dirbti su silpnai suformatuotais tekstiniais duomenimis, tai yra žurnalas. O pati Node.js infrastruktūra, kaip užpakalinė platforma, leidžia lengvai ir patogiai dirbti su tinklo ryšiais ir iš tiesų su bet kokiais duomenų srautais.

Atitinkamai „ištempiame“ du ryšius: pirmasis „klauso“ paties rąsto ir pasiima jį sau, o antrasis – periodiškai paklausti bazės. „Bet žurnalas rodo, kad ženklas su 123 id yra užblokuotas“, tačiau kūrėjui tai nieko nereiškia, todėl būtų malonu paklausti duomenų bazės: „Kas vis tiek yra OID = 123? Ir todėl mes periodiškai klausiame bazės to, ko dar nežinome apie save.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

„Yra tik vienas dalykas, į kurį neatsižvelgei, yra į dramblį panašių bičių rūšis!..“ Pradėjome kurti šią sistemą, kai norėjome stebėti 10 serverių. Mūsų supratimu, kritiškiausia, kai iškilo problemų, kurias buvo sunku išspręsti. Bet per pirmąjį ketvirtį už stebėjimą gavome šimtuką – nes sistema veikė, visi norėjo, visiems buvo patogu.

Visa tai reikia sumuoti, duomenų srautas didelis ir aktyvus. Tiesą sakant, tai, ką stebime, su kuo galime susidoroti, tą ir naudojame. Mes taip pat naudojame PostgreSQL kaip duomenų saugyklą. Ir niekas negali greičiau „įpilti“ duomenų nei operatorius COPY Dar ne.

Tačiau tiesiog „išpilti“ duomenis iš tikrųjų nėra mūsų technologija. Nes jei turite maždaug 50 100 užklausų per sekundę šimte serverių, tai sugeneruos 150–XNUMX GB žurnalų per dieną. Todėl turėjome atsargiai „nupjauti“ pagrindą.

Pirma, mes padarėme skirstymas pagal dieną, nes apskritai niekam neįdomu koreliacija tarp dienų. Koks skirtumas, ką turėjote vakar, jei šįvakar išleidote naują programos versiją ir jau šiek tiek naujos statistikos.

Antra, mes išmokome (buvome priversti) labai, labai greitai rašoma naudojant COPY. Tai yra, ne tik COPYnes jis greitesnis už INSERT, ir dar greičiau.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Trečias punktas – privalėjau atitinkamai atsisakyti trigerių ir užsienio raktų. Tai yra, mes neturime jokio nuorodinio vientisumo. Nes jei turite lentelę, kurioje yra FK pora, ir duomenų bazės struktūroje sakote, kad „čia yra žurnalo įrašas, kurį FK nurodo, pavyzdžiui, į įrašų grupę“, tada, kai ją įterpiate, PostgreSQL nelieka nieko kito, kaip tai imti ir padaryti sąžiningai SELECT 1 FROM master_fk1_table WHERE ... su identifikatoriumi, kurį bandote įterpti – tik norėdami patikrinti, ar šis įrašas ten yra, ar „nenulaužote“ šio svetimo rakto įdėdami.

Vietoj vieno įrašo į tikslinę lentelę ir jos indeksus gauname papildomą naudą skaitydami visas lenteles, į kurias ji nurodo. Bet mums to visai nereikia - mūsų užduotis yra įrašyti kuo daugiau ir kuo greičiau su mažiausia apkrova. Taigi FK – žemyn!

Kitas punktas yra agregavimas ir maišos nustatymas. Iš pradžių juos įdiegėme duomenų bazėje - juk patogu iš karto, atėjus įrašui, padaryti tai kokioje nors planšetėje „plius vienas“ tiesiai gaidiklyje. Na, tai patogu, bet tas pats blogas dalykas - įdedate vieną įrašą, bet esate priversti skaityti ir rašyti ką nors kita iš kitos lentelės. Be to, jūs ne tik skaitote ir rašote, bet ir darote tai kiekvieną kartą.

Dabar įsivaizduokite, kad turite lentelę, kurioje tiesiog suskaičiuojate užklausų, perduotų per konkretų pagrindinį kompiuterį, skaičių: +1, +1, +1, ..., +1. Ir iš principo jums to nereikia - viskas įmanoma suma kolektoriaus atmintyje ir vienu ypu nusiųsti į duomenų bazę +10.

Taip, iškilus tam tikroms problemoms, jūsų loginis vientisumas gali „sugriūti“, bet tai beveik nerealus atvejis – nes turite normalų serverį, jo valdiklyje yra baterija, turite operacijų žurnalą, žurnalą failų sistema... Apskritai, tai nėra verta. Produktyvumo praradimas, kurį gaunate paleisdami trigerius / FK, nėra vertas jūsų patiriamų išlaidų.

Tas pats ir su maiša. Pas tave atskrenda tam tikra užklausa, iš jos duomenų bazėje suskaičiuoji tam tikrą identifikatorių, įrašai į duomenų bazę ir pasakai visiems. Viskas gerai, kol įrašymo metu pas tave ateina antras žmogus, kuris nori įrašyti tą patį – ir tave užblokuoja, o tai jau blogai. Todėl, jei kai kurių ID generavimą galite perkelti klientui (palyginti su duomenų baze), geriau tai padaryti.

Mums tiesiog puikiai tiko naudoti MD5 iš teksto - užklausa, planas, šablonas,... Skaičiuojame kolektoriaus pusėje, o jau paruoštą ID “įpilame” į duomenų bazę. MD5 ilgis ir kasdienis skaidymas leidžia nesijaudinti dėl galimų susidūrimų.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Tačiau norint greitai visa tai įrašyti, reikėjo modifikuoti pačią įrašymo procedūrą.

Kaip dažniausiai rašote duomenis? Turime kažkokį duomenų rinkinį, suskaidome į kelias lenteles, o paskui KOPIJUOJAM - pirma į pirmą, paskui į antrą, į trečią... Nepatogu, nes atrodo, kad vieną duomenų srautą rašome trimis žingsniais. nuosekliai. Nemalonus. Ar galima tai padaryti greičiau? Gali!

Norėdami tai padaryti, pakanka tik suskaidyti šiuos srautus lygiagrečiai vienas su kitu. Pasirodo, turime klaidų, užklausų, šablonų, blokavimų,... skraidančių atskirose gijose – ir visa tai rašome lygiagrečiai. Užteks šitam laikykite COPY kanalą nuolat atidarytą kiekvienai atskirai tikslinei lentelei.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Tai yra, kolekcinėje visada yra upelis, į kurią galiu įrašyti man reikalingus duomenis. Bet kad duomenų bazė pamatytų šiuos duomenis ir kas nors neužstrigtų laukdamas, kol šie duomenys bus parašyti, COPY turi būti pertraukiamas tam tikrais intervalais. Mums pats efektyviausias laikotarpis buvo apie 100ms – uždarome ir iškart vėl atidarome prie to paties stalo. Ir jei kai kurių piko metu mums neužtenka vieno srauto, mes atliekame telkimą iki tam tikros ribos.

Be to, išsiaiškinome, kad tokiam apkrovos profiliui bet koks agregavimas, kai įrašai renkami partijomis, yra blogis. Klasikinis blogis yra INSERT ... VALUES ir dar 1000 įrašų. Nes tuo metu jūs turite rašymo piką laikmenoje, o visi kiti, bandantys ką nors įrašyti į diską, lauks.

Norėdami atsikratyti tokių anomalijų, tiesiog nieko nesudėkite, visai nebuferuokite. Ir jei įvyksta buferis į diską (laimei, Stream API Node.js leidžia tai išsiaiškinti) – atidėkite šį prisijungimą. Gavę įvykį, kad jis vėl nemokamas, rašykite į jį iš susikaupusios eilės. O kol jis užimtas, paimkite kitą laisvą iš baseino ir parašykite į jį.

Prieš taikydami tokį duomenų įrašymo metodą, turėjome maždaug 4K rašymo operacijų ir tokiu būdu apkrovą sumažinome 4 kartus. Dabar dėl naujų stebimų duomenų bazių jos išaugo dar 6 kartus – iki 100MB/s. Ir dabar mes saugome paskutinių 3 mėnesių žurnalus apie 10–15 TB, tikėdamiesi, kad tik po trijų mėnesių bet kuris kūrėjas galės išspręsti bet kokią problemą.

Mes suprantame problemas

Tačiau tiesiog surinkti visus šiuos duomenis yra gerai, naudinga, aktualu, bet nepakanka – tai reikia suprasti. Nes tai milijonai skirtingų planų per dieną.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Tačiau milijonai yra nevaldomi, pirmiausia turime padaryti „mažesnius“. Ir, visų pirma, turite nuspręsti, kaip sutvarkysite šį „mažesnį“ dalyką.

Mes nustatėme tris pagrindinius dalykus:

  • kas išsiuntė šį prašymą
    Tai yra, iš kokios programos ji „atkeliavo“: žiniatinklio sąsajos, užpakalinės programos, mokėjimo sistemos ar dar kažko.
  • kur tai atsitiko
    Kokiame konkrečiame serveryje? Nes jei po viena programa turi kelis serverius, o staiga vienas „apkvailina“ (nes „supuvęs diskas“, „nutekėjo atmintis“, dar kokia problema), tuomet reikia konkrečiai kreiptis į serverį.
  • kaip problema vienaip ar kitaip pasireiškė

Norėdami suprasti, „kas“ atsiuntė mums užklausą, naudojame standartinį įrankį – nustatome seanso kintamąjį: SET application_name = '{bl-host}:{bl-method}'; — siunčiame verslo logikos pagrindinio kompiuterio, iš kurio gaunama užklausa, pavadinimą ir ją inicijavusio metodo ar programos pavadinimą.

Po to, kai perduodame užklausos „savininką“, ji turi būti išvesta į žurnalą - tam sukonfigūruojame kintamąjį log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Besidomintiems gal pažiūrėk vadoveką visa tai reiškia. Pasirodo, žurnale matome:

  • laikas
  • procesų ir operacijų identifikatoriai
  • duomenų bazės pavadinimas
  • Asmens, atsiuntusio šią užklausą, IP
  • ir metodo pavadinimą

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Tada supratome, kad nėra labai įdomu žiūrėti į vienos užklausos koreliaciją tarp skirtingų serverių. Nedažnai pasitaiko situacija, kai viena programa čia ir ten vienodai sugenda. Bet net jei tai tas pats, pažiūrėkite į bet kurį iš šių serverių.

Taigi čia yra pjūvis "vienas serveris - viena diena" mums jo pakanka bet kokiai analizei.

Pirmoji analitinė dalis yra tokia pati "pavyzdys" - sutrumpinta plano pateikimo forma, išvalyta nuo visų skaitinių rodiklių. Antrasis pjūvis yra programa arba metodas, o trečiasis - konkretus plano mazgas, dėl kurio kilo problemų.

Kai perėjome nuo konkrečių atvejų prie šablonų, iš karto gavome du pranašumus:

  • daugkartinis analizuojamų objektų skaičiaus sumažinimas
    Turime analizuoti problemą jau ne pagal tūkstančius užklausų ar planų, o pagal dešimtis šablonų.
  • laiko juosta
    Tai yra, apibendrindami „faktus“ tam tikroje skiltyje, galite parodyti jų išvaizdą dienos metu. Ir čia jūs galite suprasti, kad jei turite kažkokį modelį, kuris vyksta, pavyzdžiui, kartą per valandą, bet tai turėtų vykti kartą per dieną, turėtumėte pagalvoti apie tai, kas nutiko - kas tai sukėlė ir kodėl, gal tai turėtų būti čia neturėtų. Tai dar vienas neskaitinis, grynai vizualinis analizės metodas.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Likę metodai yra pagrįsti rodikliais, kuriuos ištraukiame iš plano: kiek kartų toks modelis įvyko, bendras ir vidutinis laikas, kiek duomenų buvo nuskaityta iš disko ir kiek iš atminties...

Nes, pavyzdžiui, ateini į pagrindinio kompiuterio analitikos puslapį, žiūrėk – diske kažkas pradeda skaityti per daug. Serveryje esantis diskas negali jo valdyti – kas iš jo skaito?

O jūs galite rūšiuoti pagal bet kurį stulpelį ir nuspręsti, su kuo dabar susidursite - ar procesoriaus ar disko apkrova, ar bendras užklausų skaičius... Surūšiavome, pažiūrėjome į „topus“, sutvarkėme ir išleido naują programos versiją.
[vaizdo paskaita]

Ir iš karto galite pamatyti skirtingas programas, kurios pateikiamos su tuo pačiu šablonu pagal užklausą, pvz., SELECT * FROM users WHERE login = 'Vasya'. Frontend, backend, apdorojimas... Ir jums įdomu, kodėl apdorojimas skaitytų vartotoją, jei jis su juo nebendrauja.

Priešingas būdas yra iš karto pamatyti, ką ji daro. Pavyzdžiui, frontend yra tai, tai, tai ir tai kartą per valandą (padeda laiko juosta). Ir iš karto kyla klausimas: atrodo, kad ne frontendės darbas ką nors daryti kartą per valandą...

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Po kurio laiko supratome, kad mums trūksta apibendrinimo statistika pagal plano mazgus. Iš planų išskyrėme tik tuos mazgus, kurie kažką daro su pačių lentelių duomenimis (skaito/rašo juos pagal indeksą ar ne). Tiesą sakant, prie ankstesnės nuotraukos pridedamas tik vienas aspektas - kiek įrašų šis mazgas mums atnešė?, ir kiek buvo atmesta (filtro pašalintos eilutės).

Neturite tinkamo indekso plokštelėje, pateikiate jam užklausą, ji praskrieja už indekso, patenka į Seq Scan... išfiltravote visus įrašus, išskyrus vieną. Kodėl jums reikia 100 mln. filtruotų įrašų per dieną? Ar ne geriau susukti indeksą?

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Išanalizavę visus planus mazgas po mazgo, supratome, kad planuose yra keletas tipiškų struktūrų, kurios gali atrodyti įtartinai. Ir būtų malonu kūrėjui pasakyti: „Draugai, čia jūs pirmiausia perskaitote pagal indeksą, tada rūšiuojate, o tada nupjaunate“ - paprastai yra vienas įrašas.

Kiekvienas, parašęs užklausas, tikriausiai yra susidūręs su šiuo modeliu: „Duokite man paskutinį Vasya užsakymą, jo datą.“ Ir jei neturite rodyklės pagal datą arba naudojamoje rodyklėje nėra datos, tada jūs žengti lygiai ant to paties „grėblio“ .

Bet mes žinome, kad tai yra „grėblys“ – tai kodėl iš karto nepasakius kūrėjui, ką jis turėtų daryti. Atitinkamai, dabar atidarydamas planą mūsų kūrėjas iš karto pamato gražų paveikslėlį su patarimais, kur jam iškart sakoma: „Turi problemų ir ten, ir ten, bet jos sprendžiamos taip ir taip“.

Dėl to patirties, kurios reikėjo sprendžiant problemas pradžioje ir dabar, gerokai sumažėjo. Štai tokį įrankį turime.

Masinis PostgreSQL užklausų optimizavimas. Kirilas Borovikovas (Tenzoras)

Šaltinis: www.habr.com

Добавить комментарий