PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Aruandes esitatakse mõned lähenemisviisid, mis võimaldavad jälgida SQL-päringute toimivust, kui neid on päevas miljoneid, ja seal on sadu jälgitavaid PostgreSQL-servereid.

Millised tehnilised lahendused võimaldavad meil sellist infomahtu tõhusalt töödelda ja kuidas see tavalise arendaja elu lihtsamaks teeb?


Kes on huvitatud? konkreetsete probleemide analüüs ja erinevad optimeerimistehnikad SQL-päringud ja tüüpiliste DBA-probleemide lahendamine PostgreSQL-is – saate ka loe artiklite seeriat sellel teemal.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)
Minu nimi on Kirill Borovikov, ma esindan Tensor firma. Täpsemalt olen spetsialiseerunud meie ettevõtte andmebaasidega töötamisele.

Täna räägin teile, kuidas optimeerime päringuid, kui te ei pea ühe päringu toimivust "lahti valima", vaid probleemi massiliselt lahendama. Kui taotlusi on miljoneid ja peate mõned leidma lähenemised lahendusele see suur probleem.

Üldiselt on Tensor miljonile meie kliendile VLSI on meie rakendus: ettevõtte sotsiaalvõrgustik, lahendused videosuhtluseks, sise- ja välisdokumentide liikumiseks, raamatupidamissüsteemid raamatupidamisele ja ladudele,... Ehk siis selline integreeritud ärijuhtimise “megakombinaat”, milles on üle 100 erineva siseprojektid.

Tagamaks, et need kõik töötaksid ja areneksid normaalselt, on meil üle riigi 10 arenduskeskust, rohkem neist 1000 arendajat.

Oleme PostgreSQL-iga töötanud alates 2008. aastast ja kogunud suurel hulgal seda, mida töötleme – kliendiandmeid, statistilisi, analüütilisi, andmeid välistest infosüsteemidest – rohkem kui 400 TB. Ainuüksi tootmises on umbes 250 serverit ja kokku on umbes 1000 andmebaasiserverit, mida jälgime.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

SQL on deklaratiivne keel. Te ei kirjelda mitte "kuidas" miski peaks töötama, vaid "mida" soovite saavutada. DBMS teab paremini, kuidas JOIN-i teha – kuidas tabeleid ühendada, milliseid tingimusi kehtestada, mis indeksist läbi läheb, mis mitte...

Mõned DBMS-id aktsepteerivad vihjeid: "Ei, ühendage need kaks tabelit sellises ja sellises järjekorras", kuid PostgreSQL ei saa seda teha. See on juhtivate arendajate teadlik seisukoht: "Pigem lõpetame päringu optimeerija, kui lubame arendajatel mingeid vihjeid kasutada."

Kuid hoolimata asjaolust, et PostgreSQL ei lase "väljaspool" ennast kontrollida, lubab see suurepäraselt vaata, mis tema sees toimubmillal oma päringut käitate ja kus sellega probleeme on.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Millised klassikalised probleemid üldiselt [DBA-le] arendajal tavaliselt kaasnevad? “Siin täitsime palve ja meiega on kõik aeglane, kõik ripub, midagi toimub... Mingi häda!”

Põhjused on peaaegu alati samad:

  • ebaefektiivne päringualgoritm
    Arendaja: "Nüüd annan talle 10 tabelit SQL-is JOIN-i kaudu..." - ja loodab, et tema tingimused saavad imekombel tõhusalt lahti ja ta saab kõik kiiresti kätte. Aga imesid ei juhtu ja iga sellise varieeruvusega süsteem (10 tabelit ühes FROMis) annab alati mingi vea. [artikkel]
  • ebaoluline statistika
    See punkt on väga asjakohane just PostgreSQL-i jaoks, kui "valasite" serverisse suure andmestiku, esitate päringu ja see "sekskaneerib" teie tahvelarvuti. Sest eile oli selles 10 kirjet ja täna 10 miljonit, kuid PostgreSQL pole sellest veel teadlik ja me peame talle sellest rääkima. [artikkel]
  • "pistikuga" ressursse
    Olete installinud suure ja koormatud andmebaasi nõrgasse serverisse, millel pole piisavalt ketta, mälu või protsessori jõudlust. Ja see on kõik... Kuskil on jõudluse lagi, millest kõrgemale ei saa enam hüpata.
  • blokeerimine
    See on keeruline punkt, kuid need on kõige asjakohasemad erinevate muutmispäringute jaoks (INSERT, UPDATE, DELETE) - see on eraldi suur teema.

Plaani saamine

...Ja kõige muu jaoks meie vaja plaani! Peame nägema, mis serveris toimub.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

PostgreSQL-i päringu täitmisplaan on tekstiesituses päringu täitmisalgoritmi puu. Just algoritm osutus planeerija analüüsi tulemusena kõige tõhusamaks.

Iga puusõlm on toiming: andmete hankimine tabelist või indeksist, bitmapi koostamine, kahe tabeli ühendamine, ühendamine, ristumine või valikute välistamine. Päringu täitmine hõlmab selle puu sõlmede läbimist.

Päringuplaani saamiseks on kõige lihtsam viis täita lause EXPLAIN. Kõigi reaalsete atribuutidega hankimiseks, st tegeliku päringu käivitamiseks baasil - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Halb osa: selle käivitamisel juhtub see "siin ja praegu", seega sobib see ainult kohalikuks silumiseks. Kui võtate suure koormusega serveri, mis on tugeva andmevahetuse all, ja näete: „Oh! Siin on meil aeglane täitminexia palve." Pool tundi, tund tagasi – sel ajal, kui sa jooksid ja said selle päringu logidest ning tõid selle tagasi serverisse, muutus kogu sinu andmestik ja statistika. Käivitate seda silumiseks – ja see töötab kiiresti! Ja sa ei saa aru, miks, miks see oli aeglaselt.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Et aru saada, mis täpselt juhtus sel hetkel, kui palve serveris täideti, kirjutasid targad inimesed auto_explain moodul. See on olemas peaaegu kõigis levinumates PostgreSQL-i distributsioonides ja selle saab lihtsalt konfiguratsioonifailis aktiveerida.

Kui ta mõistab, et mõni päring kestab kauem kui teie poolt määratud limiit, teeb see seda "hetktõmmis" selle päringu plaanist ja kirjutab need logisse kokku.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Nüüd tundub kõik korras olevat, läheme palgi juurde ja vaatame seal... [tekst jalalapp]. Kuid me ei saa selle kohta midagi öelda, peale selle, et see on suurepärane plaan, sest selle täitmine võttis aega 11 ms.

Tundub, et kõik on korras, kuid miski pole selge, mis tegelikult juhtus. Peale üldise aja ei näe me tegelikult midagi. Sest sellise lihtteksti “lamba” vaatamine ei ole üldjuhul visuaalne.

Kuid isegi kui see pole ilmne, isegi kui see on ebamugav, on põhimõttelisemaid probleeme:

  • Sõlm näitab kogu alampuu ressursside summa tema all. See tähendab, et te ei saa lihtsalt teada, kui palju aega sellele konkreetsele indeksi skannimisele kulutasite, kui selle all on mõni pesastatud tingimus. Peame dünaamiliselt vaatama, kas sees on “lapsed” ja tingimuslikud muutujad, CTE-d – ja lahutama kõik need “meie meeles”.
  • Teine punkt: sõlmel näidatud aeg on ühe sõlme täitmise aeg. Kui see sõlm käivitati näiteks mitu korda tabelikirjete läbimise tulemusena, suureneb plaanis silmuste - selle sõlme tsüklite - arv. Kuid aatomi täitmisaeg ise jääb plaani mõttes samaks. See tähendab, et selleks, et mõista, kui kaua seda sõlme kokku tehti, peate korrutama ühe asja teisega - jällegi "oma peas".

Sellistes olukordades mõistke "Kes on nõrgim lüli?" peaaegu võimatu. Seetõttu kirjutavad isegi arendajad ise "käsiraamatus", et "Plaanist aru saamine on kunst, mida tuleb õppida, kogeda...".

Kuid meil on 1000 arendajat ja te ei saa seda kogemust igaühele edasi anda. Mina, sina, tema tean, aga keegi seal enam ei tea. Võib-olla ta õpib, või võib-olla mitte, aga tal on praegu vaja tööd teha – ja kust ta selle kogemuse saaks?

Plaani visualiseerimine

Seetõttu mõistsime, et nende probleemidega toimetulemiseks vajame plaani hea visualiseerimine. [artikkel]

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Esmalt käisime "turult läbi" - vaatame Internetist, mis üldse olemas on.

Kuid selgus, et suhteliselt "elavaid" lahendusi, mis enam-vähem arenevad, on väga vähe - sõna otseses mõttes ainult üks: seleta.depesz.com autor Hubert Lubaczewski. Kui sisestate väljale "voog" plaani tekstiesituse, kuvatakse see parsitud andmetega tabel:

  • sõlme enda töötlemisaeg
  • kogu alampuu koguaeg
  • leitud kirjete arv, mis olid statistiliselt oodatud
  • sõlme keha ise

Sellel teenusel on ka võimalus jagada linkide arhiivi. Sa viskasid oma plaani sinna ja ütlesid: "Hei, Vasja, siin on link, seal on midagi valesti."

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Kuid on ka väikseid probleeme.

Esiteks tohutul hulgal “kopeeri-kleebi”. Võtad tüki palgist, torkad selle sinna sisse ja veel ja veel.

Teiseks ei analüüsi loetud andmete hulka — samad puhvrid, mis väljastavad EXPLAIN (ANALYZE, BUFFERS), me ei näe seda siin. Ta lihtsalt ei tea, kuidas neid lahti võtta, neist aru saada ja nendega koostööd teha. Kui loete palju andmeid ja mõistate, et võite ketta ja mälu vahemälu valesti paigutada, on see teave väga oluline.

Kolmas negatiivne punkt on selle projekti väga nõrk areng. Kommitatsioonid on väga väikesed, on hea, kui kord kuue kuu jooksul ja kood on Perlis.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Kuid see kõik on "laulusõnad", me võiksime sellega kuidagi elada, kuid on üks asi, mis meid sellest teenusest suuresti eemale pööras. Need on vead Common Table Expressioni (CTE) ja erinevate dünaamiliste sõlmede (nt InitPlan/SubPlan) analüüsis.

Kui seda pilti uskuda, siis on iga üksiku sõlme kogu täitmisaeg pikem kui kogu päringu täitmise koguaeg. See on lihtne - selle CTE genereerimise aega ei lahutatud CTE Scan sõlmest. Seetõttu ei tea me enam õiget vastust, kui kaua CTE-skannimine ise aega võttis.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Siis saime aru, et on aeg kirjutada oma - hurraa! Iga arendaja ütleb: "Nüüd kirjutame ise, see saab olema imelihtne!"

Võtsime kasutusele veebiteenustele tüüpilise virna: Node.js + Expressil põhinev tuum, kaunite diagrammide jaoks kasutasime Bootstrapi ja D3.js-i. Ja meie ootused olid täielikult õigustatud - saime esimese prototüübi 2 nädalaga:

  • kohandatud plaani parser
    See tähendab, et nüüd saame analüüsida mis tahes plaani PostgreSQL-i loodud plaanidest.
  • dünaamiliste sõlmede õige analüüs - CTE skaneerimine, InitPlan, SubPlan
  • puhvrite jaotuse analüüs - kus loetakse andmelehti mälust, kus lokaalsest vahemälust, kus kettalt
  • sai selgust
    Et seda kõike mitte logis “kaevata”, vaid näha pildil kohe “nõrgim lüli”.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Meil on midagi sellist koos süntaksi esiletõstmisega. Kuid tavaliselt ei tööta meie arendajad enam plaani täieliku esitusega, vaid lühema plaaniga. Oleme ju kõik numbrid juba parseldanud ja vasakule-paremale loopinud ning keskele jätsime ainult esimese rea, mis sõlm see on: CTE Scan, CTE genereerimine või Seq Scan mingi märgi järgi.

See on lühendatud esitus, mida me nimetame plaani mall.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Mis oleks veel mugav? Oleks mugav näha, milline osa meie koguajast millisele sõlmele on eraldatud – ja lihtsalt “kleepida” kõrvale pirukaart.

Osutame sõlmele ja näeme – selgub, et Seq Scanile kulus koguajast vähem kui veerand ja ülejäänud 3/4 võttis CTE Scan. Õudus! See on väike märkus CTE Scani "tulekiiruse" kohta, kui kasutate neid oma päringutes aktiivselt. Need ei ole väga kiired – jäävad alla isegi tavalisele tabeli skaneerimisele. [artikkel] [artikkel]

Aga tavaliselt on sellised diagrammid huvitavamad, keerukamad, kui osutame kohe lõigule ja näeme näiteks, et üle poole ajast mõni Seq Scan “sööb”. Pealegi oli sees mingi Filter, selle järgi visati palju plaate ära... Võid selle pildi otse arendajale visata ja öelda: “Vasja, siin on sul kõik halvasti! Mõelge välja, vaadake - midagi on valesti!"

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Loomulikult olid asjaga seotud mõned “rehad”.

Esimese asjana puutusime kokku ümardamisprobleemiga. Plaani iga üksiku sõlme aeg on näidatud 1 μs täpsusega. Ja kui sõlmede tsüklite arv ületab näiteks 1000 - pärast täitmist PostgreSQL jagatakse "täpsuse piires", siis tagasi arvutamisel saame koguaja "kuskil 0.95 ms ja 1.05 ms vahel". Kui loendus läheb mikrosekunditesse, on see okei, aga kui see on juba [milli]sekundit, peate selle teabega arvestama ressursside lahtisidumisel plaani "kes kui palju tarbis" sõlmedesse.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Teine, keerulisem punkt on ressursside (need puhvrid) jaotamine dünaamiliste sõlmede vahel. See läks meile maksma prototüübi esimesed 2 nädalat pluss veel 4 nädalat.

Sellise probleemi tekkimine on üsna lihtne – teeme CTE-d ja väidetavalt loeme sellest midagi. Tegelikult on PostgreSQL "tark" ega loe sealt midagi otse välja. Siis võtame sellelt esimese plaadi ja sellele sama CTE saja esimese plaadi.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Vaatame plaani ja mõistame - see on kummaline, meil on Seq Scanis "tarvitatud" 3 puhvrit (andmelehte), CTE Scanis veel 1 ja teises CTE Scanis veel 2. See tähendab, et kui me kõik lihtsalt kokku võtame, saame 6, kuid tahvelarvutist loeme ainult 3! CTE Scan ei loe kuskilt midagi, vaid töötab otse protsessimäluga. See tähendab, et siin on midagi selgelt valesti!

Tegelikult selgub, et siin on kõik need 3 lehekülge andmeid, mis Seq Scanist küsiti, kõigepealt küsiti 1. CTE skannimist ja siis 1. ning talle loeti veel 2. Ehk siis kokku 2 lehekülge loeti andmeid, mitte 3.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Ja see pilt viis meid arusaamisele, et plaani täitmine pole enam puu, vaid lihtsalt mingi atsükliline graafik. Ja saime sellise diagrammi, et saaksime aru, „mis kust üldse tuli”. See tähendab, et siin lõime pg_classist CTE ja küsisime seda kaks korda ning peaaegu kogu meie aeg kulus harule, kui seda teist korda küsisime. Selge see, et 2. sissekande lugemine on palju kallim kui lihtsalt tahvelarvutist 101. kirje lugemine.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Hingasime korraks välja. Nad ütlesid: "Nüüd, Neo, sa tead kung fu'd! Nüüd on meie kogemus otse teie ekraanil. Nüüd saate seda kasutada." [artikkel]

Logide konsolideerimine

Meie 1000 arendajat hingasid kergendatult. Kuid saime aru, et meil on ainult sadu “võitlusservereid” ja kogu see arendajatepoolne “kopeeri-kleebi” pole sugugi mugav. Saime aru, et peame selle ise koguma.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Üldiselt on olemas standardmoodul, mis suudab statistikat koguda, kuid see tuleb ka konfiguratsioonis aktiveerida - see pg_stat_statements moodul. Aga meile ta ei sobinud.

Esiteks määrab see samadele päringutele, kasutades samas andmebaasis erinevaid skeeme erinevad päringu ID-d. See tähendab, et kui teete seda kõigepealt SET search_path = '01'; SELECT * FROM user LIMIT 1;ja siis SET search_path = '02'; ja sama päring, siis selle mooduli statistikas on erinevad sissekanded ja ma ei saa koguda üldist statistikat konkreetselt selle päringuprofiili kontekstis, ilma skeeme arvestamata.

Teine punkt, mis takistas meil seda kasutamast, on plaanide puudumine. See tähendab, et plaani pole, on ainult taotlus ise. Me näeme, mis aeglustus, kuid me ei mõista, miks. Ja siin pöördume tagasi kiiresti muutuva andmestiku probleemi juurde.

Ja viimane hetk - "faktide" puudumine. See tähendab, et te ei saa käsitleda konkreetset päringu täitmise eksemplari – seda pole, on ainult koondatud statistika. Kuigi sellega on võimalik töötada, on see lihtsalt väga raske.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Seetõttu otsustasime copy-paste’iga võidelda ja hakkasime kirjutama koguja.

Koguja loob ühenduse SSH kaudu, loob sertifikaadi abil andmebaasiga turvalise ühenduse serveriga ja tail -F "klammerdub" selle külge logifailis. Nii et sellel seansil saame kogu logifaili täieliku "peegli"., mille server genereerib. Serveri enda koormus on minimaalne, sest me ei parsi seal midagi, vaid peegeldame liiklust.

Kuna olime juba alustanud Node.js-i liidese kirjutamist, jätkasime sellesse koguja kirjutamist. Ja see tehnoloogia on ennast õigustanud, sest väga mugav on kasutada JavaScripti, et töötada nõrgalt vormindatud tekstiandmetega, milleks on logi. Ja Node.js-i infrastruktuur ise kui taustaplatvorm võimaldab teil lihtsalt ja mugavalt töötada võrguühendustega ja tõepoolest kõigi andmevoogudega.

Vastavalt sellele "venitame" kahte ühendust: esimene selleks, et "kuulaks" palki ennast ja viiks selle enda juurde, ja teine, et perioodiliselt baasilt küsida. "Aga logi näitab, et oidiga 123 märk on blokeeritud", kuid see ei tähenda arendajale midagi ja oleks tore andmebaasist küsida: "Mis on ikkagi OID = 123?" Ja seetõttu küsime baasilt perioodiliselt seda, mida me enda kohta veel ei tea.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

"On ainult üks asi, millega te ei arvestanud, seal on elevandilaadsete mesilaste liik!..." Alustasime selle süsteemi arendamist, kui tahtsime jälgida 10 serverit. Meie mõistes kõige kriitilisem, kus tekkisid mõned probleemid, millega oli raske toime tulla. Aga esimese kvartali jooksul saime jälgimise eest sada - kuna süsteem töötas, siis kõik tahtsid, kõigil oli mugav.

Kõik see tuleb kokku liita, andmevoog on suur ja aktiivne. Tegelikult on see, mida me jälgime, millega saame hakkama, see on see, mida me kasutame. Andmesalvestusena kasutame ka PostgreSQL-i. Ja miski pole kiirem sellesse andmete "valamiseks" kui operaator COPY Mitte veel.

Kuid lihtsalt andmete "valamine" pole tegelikult meie tehnoloogia. Sest kui teil on sajal serveril umbes 50 100 päringut sekundis, genereerib see päevas 150–XNUMX GB logisid. Seetõttu pidime aluse hoolikalt “lõigama”.

Esiteks, me tegime päevade kaupa jaotamine, sest üldiselt ei huvita päevade vaheline korrelatsioon kedagi. Mis vahet on sellel, mis teil eile oli, kui täna õhtul lasite välja rakenduse uue versiooni – ja juba uut statistikat.

Teiseks õppisime (olime sunnitud) väga-väga kiire kirjutamine kasutades COPY. See tähendab, et mitte ainult COPYsest ta on kiirem kui INSERT, ja veelgi kiiremini.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Kolmas punkt – pidin loobuma vastavalt päästikutest ja võõrvõtmetest. See tähendab, et meil puudub viiteline terviklikkus. Sest kui teil on tabel, millel on paar FK-d ja te ütlete andmebaasi struktuuris, et "siin on logikirje, millele FK viitab näiteks kirjete rühmale", siis selle sisestamisel PostgreSQL ei jää muud üle, kui võtta ja ausalt teha SELECT 1 FROM master_fk1_table WHERE ... identifikaatoriga, mida proovite sisestada - lihtsalt selleks, et kontrollida, kas see kirje on seal olemas, et te ei katkestaks seda võõrvõtit oma sisestamisega.

Sihttabeli ja selle indeksite ühe kirje asemel saame lisakasu kõigi tabelite lugemisest, millele see viitab. Kuid me ei vaja seda üldse - meie ülesanne on salvestada võimalikult palju ja võimalikult kiiresti väikseima koormusega. Seega FK – alla!

Järgmine punkt on liitmine ja räsimine. Algselt juurutasime need andmebaasi - lõppude lõpuks on mugav seda kohe, kui kirje saabub, teha seda mingis tahvelarvutis "pluss üks" otse päästikus. Noh, see on mugav, kuid sama halb asi - sisestate ühe kirje, kuid olete sunnitud teisest tabelist midagi muud lugema ja kirjutama. Pealegi, te mitte ainult ei loe ja kirjuta, vaid teete seda ka iga kord.

Kujutage nüüd ette, et teil on tabel, milles loendate lihtsalt konkreetse hosti kaudu läbinud päringute arvu: +1, +1, +1, ..., +1. Ja põhimõtteliselt pole teil seda vaja - kõik on võimalik koguja mälus summa ja saatke ühe korraga andmebaasi +10.

Jah, teatud probleemide korral võib teie loogiline terviklikkus “lahti kukkuda”, kuid see on peaaegu ebareaalne juhtum - kuna teil on tavaline server, sellel on kontrolleris aku, teil on tehingulogi, logi failisüsteem... Üldiselt pole see seda väärt. Päästikute/FK-de käivitamisest saadav tootlikkuse kaotus ei ole teie tehtud kulutusi väärt.

Sama on räsimisega. Sinu juurde lendab kindel päring, sa arvutad selle järgi andmebaasis välja kindla identifikaatori, kirjutad selle andmebaasi ja räägid siis kõigile. Kõik on korras, kuni salvestamise ajal tuleb teie juurde teine ​​inimene, kes soovib sama asja salvestada – ja teid blokeeritakse ja see on juba halb. Seega, kui saate mõne ID genereerimise kliendile üle kanda (andmebaasi suhtes), on parem seda teha.

Meile sobis lihtsalt suurepäraselt tekstist MD5 kasutamine - päring, plaan, mall,... Arvutame koguja poolel välja, valmis ID “valame” andmebaasi. MD5 pikkus ja igapäevane jaotus võimaldavad meil mitte muretseda võimalike kokkupõrgete pärast.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Kuid selleks, et seda kõike kiiresti salvestada, pidime muutma salvestusprotseduuri ennast.

Kuidas te tavaliselt andmeid kirjutate? Meil on mingi andmestik, jagame selle mitmeks tabeliks ja siis KOPeerime - kõigepealt esimesse, siis teise, kolmandasse... See on ebamugav, sest tundub, et kirjutame ühte andmevoogu kolme sammuga. järjestikku. Ebameeldiv. Kas seda saab kiiremini teha? Saab!

Selleks piisab, kui need voolud üksteisega paralleelselt lagundada. Selgub, et meil on vead, taotlused, mallid, blokeeringud, ... eraldi lõimedes – ja me kirjutame seda kõike paralleelselt. Selleks piisab hoidke COPY kanal iga üksiku sihttabeli jaoks pidevalt avatuna.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Ehk siis kollektsionääris alati on oja, kuhu saan kirjutada vajalikud andmed. Aga et andmebaas näeks neid andmeid ja keegi ei jääks nende andmete kirjutamist ootama, COPY tuleb teatud ajavahemike järel katkestada. Meie jaoks oli kõige efektiivsem periood umbes 100ms - paneme selle kinni ja avame kohe uuesti samasse lauda. Ja kui mõne tipu ajal ühest voolust ei piisa, siis teeme kogumise teatud piirini.

Lisaks saime teada, et sellise koormusprofiili puhul on igasugune kirjete kogumine partiidena kurjast. Klassikaline kurjus on INSERT ... VALUES ja veel 1000 plaati. Sest sel hetkel on teil meediumi kirjutamise tippaeg ja kõik teised, kes üritavad kettale midagi kirjutada, ootavad.

Sellistest kõrvalekalletest vabanemiseks ärge lihtsalt ühendage midagi, ära puhverda üldse. Ja kui kettale puhverdamine toimub (õnneks võimaldab Node.js-i Stream API seda teada saada) - lükake see ühendamine edasi. Kui saate sündmuse, et see on taas tasuta, kirjutage sellele kogunenud järjekorrast. Ja kui see on hõivatud, võtke basseinist järgmine vaba ja kirjutage sellele.

Enne selle lähenemisviisi kasutuselevõttu andmete salvestamisel oli meil umbes 4K kirjutamisoperatsioone ja sel viisil vähendasime koormust 4 korda. Nüüd on need tänu uutele jälgitavatele andmebaasidele kasvanud veel 6 korda – kuni 100MB/s. Ja nüüd salvestame viimase 3 kuu logisid umbes 10–15 TB mahus, lootes, et kõigest kolme kuu pärast suudab iga arendaja mis tahes probleemi lahendada.

Me mõistame probleeme

Kuid lihtsalt kõigi nende andmete kogumine on hea, kasulik, asjakohane, kuid mitte piisav – seda tuleb mõista. Sest need on miljonid erinevad plaanid päevas.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Kuid miljonid on juhitamatud, me peame kõigepealt tegema "väiksemaid". Ja kõigepealt peate otsustama, kuidas seda "väiksemat" asja korraldada.

Oleme tuvastanud kolm põhipunkti:

  • kes saatis selle taotluse
    See tähendab, millisest rakendusest see "saabus": veebiliidesest, taustaprogrammist, maksesüsteemist või millestki muust.
  • kus see juhtus
    Millises konkreetses serveris? Sest kui sul on ühe rakenduse all mitu serverit ja järsku üks “lolli läheb” (sest “ketas on mäda”, “mälu lekkis”, mingi muu probleem), siis tuleb konkreetselt serveri poole pöörduda.
  • kui probleem ilmnes ühel või teisel viisil

Et mõista, kes meile päringu saatis, kasutame standardset tööriista – seansimuutuja määramist: SET application_name = '{bl-host}:{bl-method}'; — saadame selle äriloogika hosti nime, kust päring pärineb, ja selle algatanud meetodi või rakenduse nime.

Pärast päringu “omaniku” edastamist tuleb see logisse väljastada - selleks konfigureerime muutuja log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Huvilistele ehk vaata juhendistmida see kõik tähendab. Selgub, et näeme logis:

  • aeg
  • protsesside ja tehingute identifikaatorid
  • andmebaasi nimi
  • Selle päringu saatnud isiku IP
  • ja meetodi nimi

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Siis saime aru, et ühe päringu korrelatsiooni erinevate serverite vahel pole eriti huvitav vaadata. Ei juhtu sageli olukordi, kus üks rakendus läheb siin-seal võrdselt kokku. Kuid isegi kui see on sama, vaadake mõnda neist serveritest.

Nii et siin on lõige "üks server - üks päev" see osutus meile igasuguseks analüüsiks piisavaks.

Esimene analüütiline osa on sama "näidis" - plaani esitluse lühendatud vorm, mis on puhastatud kõigist numbrilistest näitajatest. Teine lõige on rakendus või meetod ja kolmas lõige on konkreetne plaanisõlm, mis meile probleeme tekitas.

Kui liikusime konkreetsetelt eksemplaridelt mallidele, saime korraga kaks eelist:

  • analüüsiobjektide arvu mitmekordne vähendamine
    Peame probleemi analüüsima mitte enam tuhandete päringute või plaanide, vaid kümnete mallide järgi.
  • ajaskaala
    See tähendab, et teatud jaotises "fakte" kokku võttes saate kuvada nende välimust päeva jooksul. Ja siin saate aru, et kui teil on mingi muster, mis juhtub näiteks kord tunnis, aga see peaks juhtuma kord päevas, peaksite mõtlema, mis valesti läks - kes selle põhjustas ja miks, võib-olla peaks see olema siin ei peaks. See on veel üks mittenumbriline, puhtalt visuaalne analüüsimeetod.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Ülejäänud meetodid põhinevad indikaatoritel, mille me plaanist välja võtame: mitu korda selline muster esines, kogu- ja keskmine aeg, kui palju andmeid kettalt loeti ja kui palju mälust...

Sest näiteks tuled hosti analüüsilehele, vaata - midagi hakkab kettalt liiga palju lugema. Serveris olev ketas ei saa sellega hakkama – kes sellelt loeb?

Ja saate sortida mis tahes veeru järgi ja otsustada, millega te praegu tegelete - protsessori või ketta koormus või päringute koguarv... Sorteerisime selle, vaatasime ülemiseid, parandasime ära ja avaldas rakenduse uue versiooni.
[videoloeng]

Ja kohe näete erinevaid rakendusi, millel on sama mall nagu päring SELECT * FROM users WHERE login = 'Vasya'. Frontend, backend, töötlemine... Ja te imestate, miks töötlemine loeks kasutajat, kui ta temaga ei suhtle.

Vastupidine viis on kohe näha rakendusest, mida see teeb. Näiteks frontend on see, see, see ja see kord tunnis (aitab ajaskaala). Ja kohe tekib küsimus: tundub, et esikülje ülesanne pole kord tunnis midagi teha...

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Mõne aja pärast mõistsime, et meil on puudu koondatud statistika plaani sõlmede kaupa. Isoleerisime plaanidest ainult need sõlmed, mis tabelite andmetega ise midagi teevad (loevad/kirjutavad neid indeksi järgi või mitte). Tegelikult on eelmise pildiga võrreldes lisatud ainult üks aspekt - mitu rekordit see sõlm meile tõi?ja kui palju neist loobuti (filtriga eemaldatud read).

Teil pole plaadil sobivat indeksit, esitate sellele päringu, see lendab indeksist mööda, langeb Seq Scani... olete kõik kirjed välja filtreerinud, välja arvatud üks. Miks on vaja 100 miljonit filtreeritud kirjet päevas? Kas poleks parem indeks kokku kerida?

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Olles analüüsinud kõiki plaane sõlmede kaupa, mõistsime, et plaanides on mõned tüüpilised struktuurid, mis tõenäoliselt tunduvad kahtlased. Ja oleks tore arendajale öelda: “Sõber, siin loed kõigepealt indeksi järgi, siis sorteerid ja siis lõikad ära” - reeglina on üks kirje.

Kõik, kes päringuid kirjutasid, on ilmselt kohanud seda mustrit: "Andke mulle Vasya viimane tellimus, selle kuupäev." Ja kui teil pole kuupäeva järgi indeksit või kasutatud registris pole kuupäeva, siis astu täpselt samale “rehale” .

Kuid me teame, et see on "reha" - miks mitte siis kohe arendajale öelda, mida ta peaks tegema. Vastavalt sellele näeb meie arendaja praegu plaani avades kohe ilusat näpunäidetega pilti, kus talle kohe öeldakse: "Sul on probleeme siin ja seal, aga need lahendatakse nii ja naa."

Seetõttu on alguses ja praegu probleemide lahendamiseks vajaminevate kogemuste hulk oluliselt langenud. Selline tööriist meil on.

PostgreSQL päringute hulgioptimeerimine. Kirill Borovikov (Tensor)

Allikas: www.habr.com

Lisa kommentaar