PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Raportissa esitetään joitakin lähestymistapoja, jotka mahdollistavat seurata SQL-kyselyiden suorituskykyä, kun niitä on miljoonia päivässä, ja valvottuja PostgreSQL-palvelimia on satoja.

Mitkä tekniset ratkaisut mahdollistavat tällaisen tietomäärän tehokkaan käsittelyn ja miten tämä helpottaa tavallisen kehittäjän elämää?


Ketä kiinnostaa? erityisten ongelmien analysointi ja erilaiset optimointitekniikat SQL-kyselyt ja tyypillisten DBA-ongelmien ratkaiseminen PostgreSQL:ssä - voit myös lue artikkelisarja tästä aiheesta.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)
Nimeni on Kirill Borovikov, edustan Tensor yritys. Erityisesti olen erikoistunut työskentelemään tietokantojen parissa yrityksessämme.

Tänään kerron sinulle, kuinka optimoimme kyselyt, kun sinun ei tarvitse "poimia" yhden kyselyn suorituskykyä, vaan ratkaista ongelma massalla. Kun pyyntöjä on miljoonia, ja sinun on löydettävä niitä lähestymistapoja ratkaisuun tämä suuri ongelma.

Yleensä Tensor miljoonalle asiakkaallemme on VLSI on sovelluksemme: yritysten sosiaalinen verkosto, ratkaisut videoviestintään, sisäiseen ja ulkoiseen asiakirjavirtaukseen, kirjanpitojärjestelmät kirjanpitoon ja varastoihin,... Eli sellainen integroidun liikkeenjohdon "megakombinaatti", jossa on yli 100 erilaista sisäisiä projekteja.

Varmistaaksemme, että ne kaikki toimivat ja kehittyvät normaalisti, meillä on 10 kehityskeskusta eri puolilla maata, ja niissä on enemmän 1000 kehittäjää.

Olemme työskennelleet PostgreSQL:n kanssa vuodesta 2008 ja olemme keränneet suuren määrän käsittelemäämme - asiakasdataa, tilastollista, analyyttistä, dataa ulkoisista tietojärjestelmistä - yli 400TB. Pelkästään tuotannossa on noin 250 palvelinta ja kaikkiaan noin 1000 tietokantapalvelinta, joita valvomme.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

SQL on deklaratiivinen kieli. Et kuvaile "miten" jonkin pitäisi toimia, vaan "mitä" haluat saavuttaa. DBMS tietää paremmin, miten JOIN tehdään - miten taulukot yhdistetään, mitä ehtoja asetetaan, mikä menee indeksin läpi, mikä ei...

Jotkut DBMS:t hyväksyvät vihjeet: "Ei, yhdistä nämä kaksi taulukkoa sellaiseen ja sellaiseen jonoon", mutta PostgreSQL ei voi tehdä tätä. Tämä on johtavien kehittäjien tietoinen kanta: "Mieluummin lopetamme kyselyn optimoijan kuin annamme kehittäjien käyttää jonkinlaisia ​​vihjeitä."

Mutta huolimatta siitä, että PostgreSQL ei anna "ulkopuolisen" hallita itseään, se sallii sen täydellisesti katso mitä hänen sisällään tapahtuukun suoritat kyselyäsi ja missä siinä on ongelmia.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Mitä klassisia ongelmia [DBA:lle] kehittäjällä yleensä on? "Täytimme pyynnön ja kaikki on hidasta meillä, kaikki roikkuu, jotain tapahtuu... Jonkinlainen vaiva!”

Syyt ovat lähes aina samat:

  • tehoton kyselyalgoritmi
    Kehittäjä: "Nyt annan hänelle 10 taulukkoa SQL:ssä JOINin kautta..." - ja odottaa, että hänen ehdot "vapautetaan" ihmeellisesti tehokkaasti ja hän saa kaiken nopeasti. Mutta ihmeitä ei tapahdu, ja mikä tahansa järjestelmä, jolla on tällainen vaihtelu (10 taulukkoa yhdessä FROM:issa), antaa aina jonkinlaisen virheen. [artikkeli]
  • vanhentuneet tilastot
    Tämä kohta on erittäin tärkeä erityisesti PostgreSQL:lle, kun "kaadot" suuren tietojoukon palvelimelle, teet pyynnön ja se "sexcanitsee" tabletin. Koska eilen siinä oli 10 tietuetta ja tänään 10 miljoonaa, mutta PostgreSQL ei ole vielä tietoinen tästä, ja meidän on kerrottava siitä. [artikkeli]
  • "liitä" resursseihin
    Olet asentanut suuren ja raskaasti ladatun tietokannan heikkoon palvelimeen, jolla ei ole tarpeeksi levyä, muistia tai suorittimen suorituskykyä. Ja siinä kaikki... Jossain on suorituskatto, jonka yläpuolelle ei voi enää hypätä.
  • estäminen
    Tämä on vaikea kohta, mutta ne ovat olennaisimmat erilaisille muokkauskyselyille (INSERT, UPDATE, DELETE) - tämä on erillinen iso aihe.

Suunnitelman saaminen

...Ja kaikkeen muuhun me tarvitaan suunnitelma! Meidän täytyy nähdä, mitä palvelimen sisällä tapahtuu.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Kyselyn suoritussuunnitelma PostgreSQL:lle on kyselyn suoritusalgoritmin puu tekstin esittämisessä. Juuri se algoritmi todettiin suunnittelijan analyysin tuloksena tehokkaimmaksi.

Jokainen puusolmu on operaatio: tietojen hakeminen taulukosta tai indeksistä, bittikartan rakentaminen, kahden taulukon yhdistäminen, valintojen yhdistäminen, leikkaaminen tai poissulkeminen. Kyselyn suorittamiseen kuuluu kävellä tämän puun solmujen läpi.

Kyselysuunnitelman saamiseksi helpoin tapa on suorittaa lauseke EXPLAIN. Saadaksesi kaikki todelliset attribuutit, eli suorittaaksesi kyselyn pohjalla - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Huono puoli: kun käytät sitä, se tapahtuu "tässä ja nyt", joten se sopii vain paikalliseen virheenkorjaukseen. Jos otat erittäin kuormitetun palvelimen, joka on voimakkaan tietovirran alaisena, ja näet: "Oh! Tässä meillä on hidas toteutusXia pyyntö." Puoli tuntia, tunti sitten - kun suoritit ja sait tämän pyynnön lokeista ja toit sen takaisin palvelimelle, koko tietojoukkosi ja tilastosi muuttuivat. Suoritat sen virheenkorjaukseen - ja se toimii nopeasti! Etkä voi ymmärtää miksi, miksi было hitaasti.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Älykkäät ihmiset kirjoittivat ymmärtääkseen, mitä tapahtui juuri sillä hetkellä, kun pyyntö suoritettiin palvelimella auto_explain-moduuli. Se on läsnä melkein kaikissa yleisimmissä PostgreSQL-jakeluissa, ja se voidaan yksinkertaisesti aktivoida asetustiedostossa.

Jos se huomaa, että jokin pyyntö on käynnissä pidempään kuin kerroit sille, se tekee sen "tilannekuva" tämän pyynnön suunnitelmasta ja kirjoittaa ne yhteen lokiin.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Kaikki näyttää nyt olevan hyvin, menemme puun luo ja katsomme siellä... [tekstijalkakankaa]. Mutta emme voi sanoa siitä mitään, paitsi että se on erinomainen suunnitelma, koska sen toteuttaminen kesti 11 ms.

Kaikki näyttää olevan hyvin - mutta mikään ei ole selvää, mitä todella tapahtui. Yleisen ajan lisäksi emme näe oikeastaan ​​mitään. Koska sellaisen pelkän tekstin "lammasta" katsominen ei yleensä ole visuaalista.

Mutta vaikka se ei olisikaan ilmeistä, vaikka se olisi hankalaa, on olemassa perustavanlaatuisempia ongelmia:

  • Solmu osoittaa koko alipuun resurssien summa hänen alla. Toisin sanoen et voi vain selvittää, kuinka paljon aikaa käytettiin tähän indeksiskannaukseen, jos sen alla on sisäkkäisiä ehtoja. Meidän on dynaamisesti katsottava nähdäksemme, onko sisällä "lapsia" ja ehdollisia muuttujia, CTE:itä – ja vähennettävä tämä kaikki "mielessämme".
  • Toinen kohta: solmussa ilmoitettu aika on yhden solmun suoritusaika. Jos tämä solmu suoritettiin esimerkiksi silmukan seurauksena taulukkotietueiden läpi useita kertoja, silmukoiden määrä - tämän solmun jaksot - kasvaa suunnitelmassa. Mutta itse atomin suoritusaika pysyy suunnitelman kannalta samana. Eli ymmärtääksesi kuinka kauan tämä solmu suoritettiin yhteensä, sinun on kerrottava yksi asia toisella - jälleen "päässäsi".

Tällaisissa tilanteissa ymmärrä "Kuka on heikoin lenkki?" lähes mahdotonta. Siksi jopa kehittäjät itse kirjoittavat "käsikirjaan", että "Suunnitelman ymmärtäminen on taidetta, joka on opittava, koettava...".

Mutta meillä on 1000 kehittäjää, etkä voi välittää tätä kokemusta kaikille. Minä, sinä, hän tiedät, mutta joku siellä ei enää tiedä. Ehkä hän oppii, tai ehkä ei, mutta hänen on tehtävä töitä nyt - ja mistä hän saisi tämän kokemuksen?

Suunnittele visualisointi

Siksi ymmärsimme, että näiden ongelmien ratkaisemiseksi tarvitsemme hyvä visualisointi suunnitelmasta. [artikla]

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Kävimme ensin "markkinoiden läpi" - katsotaan Internetistä, mitä on olemassa.

Mutta kävi ilmi, että suhteellisen "eläviä" ratkaisuja on hyvin vähän, jotka ovat enemmän tai vähemmän kehittyviä - kirjaimellisesti vain yksi: selittää.depesz.com Kirjailija: Hubert Lubaczewski Kun syötät "syöte"-kenttään suunnitelman tekstiesityksen, se näyttää sinulle taulukon, jossa on jäsennetyt tiedot:

  • solmun omaa käsittelyaikaa
  • koko alipuun kokonaisaika
  • haettujen tietueiden määrä, jotka olivat tilastollisesti odotettavissa
  • itse solmun runko

Tällä palvelulla on myös mahdollisuus jakaa linkkiarkisto. Heitit suunnitelmasi sinne ja sanoit: "Hei, Vasya, tässä on linkki, siellä on jotain vialla."

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Mutta on myös pieniä ongelmia.

Ensinnäkin valtava määrä "copy-pastea". Otat palan puusta, pistät sen sisään ja uudestaan ​​ja uudestaan.

Toiseksi ei analysoida luetun datan määrää - samat puskurit, jotka tuottavat EXPLAIN (ANALYZE, BUFFERS), emme näe sitä täällä. Hän ei yksinkertaisesti osaa purkaa niitä, ymmärtää niitä ja työskennellä niiden kanssa. Kun luet paljon tietoa ja huomaat, että saatat jakaa levyn ja välimuistin väärin, nämä tiedot ovat erittäin tärkeitä.

Kolmas negatiivinen kohta on tämän hankkeen erittäin heikko kehitys. Sitoumukset ovat hyvin pieniä, on hyvä, jos kerran kuudessa kuukaudessa, ja koodi on Perlissä.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Mutta tämä kaikki on "lyriikkaa", tämän kanssa voisi jotenkin elää, mutta yksi asia käänsi meidät suuresti pois tästä palvelusta. Nämä ovat virheitä Common Table Expression (CTE) -analyysissä ja useissa dynaamisissa solmuissa, kuten InitPlan/SubPlan.

Jos uskot tähän kuvaan, jokaisen yksittäisen solmun kokonaissuoritusaika on suurempi kuin koko pyynnön kokonaissuoritusaika. Se on yksinkertaista - tämän CTE:n generointiaikaa ei vähennetty CTE Scan -solmusta. Siksi emme enää tiedä oikeaa vastausta siihen, kuinka kauan itse CTE-skannaus kesti.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Sitten tajusimme, että oli aika kirjoittaa omamme - hurraa! Jokainen kehittäjä sanoo: "Nyt kirjoitamme omamme, se on erittäin helppoa!"

Otimme verkkopalveluille tyypillisen pinon: Node.js + Express -pohjaisen ytimen, käytimme Bootstrapia ja D3.js:a kauniita kaavioita varten. Ja odotuksemme olivat täysin perusteltuja - saimme ensimmäisen prototyypin 2 viikossa:

  • mukautetun suunnitelman jäsentäjä
    Eli nyt voimme jäsentää minkä tahansa suunnitelman PostgreSQL:n luomista suunnitelmista.
  • dynaamisten solmujen oikea analyysi - CTE Scan, InitPlan, SubPlan
  • puskurien jakautumisen analyysi - missä datasivuja luetaan muistista, missä paikallisesta välimuistista, missä levyltä
  • sai selvyyden
    Jotta ei "kaivaa" kaikkea tätä lokissa, vaan "heikoin lenkki" näkisi heti kuvassa.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Meillä on jotain tällaista, syntaksin korostuksen kanssa. Mutta yleensä kehittäjämme eivät enää työskentele täydellisen suunnitelman kanssa, vaan lyhyemmän suunnitelman kanssa. Olemmehan jo jäsentäneet kaikki numerot ja heitellyt niitä vasemmalle ja oikealle, ja keskelle jätimme vain ensimmäisen rivin, minkälainen solmu se on: CTE Scan, CTE-sukupolvi vai Seq Scan jonkin merkin mukaan.

Tämä on lyhennetty esitys, jota kutsumme suunnitelmamalli.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Mikä muu olisi kätevää? Olisi kätevää nähdä, mikä osuus kokonaisajastamme on varattu mille solmulle - ja vain "kiinni" se sivuun ympyrädiagrammi.

Osoitamme solmua ja näemme - käy ilmi, että Seq Scan vei alle neljänneksen kokonaisajasta ja loput 3/4 käytti CTE Scan. Kauhu! Tämä on pieni huomautus CTE Scanin "palonopeudesta", jos käytät niitä aktiivisesti kyselyissäsi. Ne eivät ole kovin nopeita - ne ovat huonompia jopa tavalliseen taulukon skannaukseen. [artikla] [artikla]

Mutta yleensä tällaiset kaaviot ovat mielenkiintoisempia, monimutkaisempia, kun osoitamme välittömästi segmenttiä ja näemme esimerkiksi, että yli puolet ajasta jokin Seq Scan "söi". Lisäksi sisällä oli jonkinlainen Filter, jonka mukaan paljon levyjä heitettiin pois... Voit heittää tämän kuvan suoraan kehittäjälle ja sanoa: "Vasya, kaikki on huonosti täällä sinulle! Ota selvää, katso - jotain on vialla!”

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Luonnollisesti mukana oli joitain "haravoja".

Ensimmäinen asia, jonka kohtasimme, oli pyöristysongelma. Suunnitelman kunkin yksittäisen solmun aika ilmoitetaan 1 μs:n tarkkuudella. Ja kun solmujaksojen määrä ylittää esimerkiksi 1000 - suorituksen jälkeen PostgreSQL jaettuna "tarkkuuden sisällä", niin takaisin laskettaessa saadaan kokonaisaika "jossain 0.95 ms ja 1.05 ms välillä". Kun laskenta menee mikrosekunteihin, se on okei, mutta kun se on jo [milli]sekuntia, sinun on otettava tämä tieto huomioon, kun "purkaat" resursseja "kuka kulutti kuinka paljon" -suunnitelman solmuihin.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Toinen, monimutkaisempi kohta on resurssien (puskurien) jakaminen dynaamisten solmujen kesken. Tämä maksoi meille prototyypin ensimmäiset 2 viikkoa ja vielä 4 viikkoa.

On melko helppoa saada tällainen ongelma - teemme CTE: n ja oletettavasti luemme siitä jotain. Itse asiassa PostgreSQL on "älykäs" eikä lue sieltä mitään suoraan. Sitten otamme siitä ensimmäisen levyn ja siihen sadan ensimmäisen samalta CTE:ltä.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Katsomme suunnitelmaa ja ymmärrämme - se on outoa, meillä on 3 puskuria (tietosivua) "kulutettuna" Seq Scanissa, 1 lisää CTE Scanissa ja 2 muuta toisessa CTE Scanissa. Eli jos vain summaamme kaiken, saamme 6, mutta tabletista luemme vain 3! CTE Scan ei lue mitään mistään, vaan toimii suoraan prosessimuistin kanssa. Eli jotain on selvästi vialla!

Itse asiassa käy ilmi, että tässä ovat kaikki ne 3 sivua tietoja, jotka pyydettiin Seq Scanista, ensin yksi pyysi ensimmäistä CTE-skannausta ja sitten toinen, ja hänelle luettiin vielä 1. Eli yhteensä 1 sivua luettiin dataa, ei 2.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Ja tämä kuva sai meidät ymmärtämään, että suunnitelman toteuttaminen ei ole enää puu, vaan yksinkertaisesti jonkinlainen asyklinen graafi. Ja saimme tällaisen kaavion, jotta ymmärrämme "mitä mistä alun perin tuli". Eli täällä loimme CTE:n pg_classista ja pyysimme sitä kahdesti, ja melkein kaikki aikamme meni haaralle, kun pyysimme sitä toisella kerralla. On selvää, että 2. merkinnän lukeminen on paljon kalliimpaa kuin pelkkä ensimmäisen merkinnän lukeminen tabletilta.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Hengitimme hetken aikaa ulos. He sanoivat: "Nyt, Neo, sinä tiedät kung fua! Nyt kokemuksemme on näytölläsi. Nyt voit käyttää sitä." [artikla]

Lokin yhdistäminen

1000 kehittäjäämme huokaisi helpotuksesta. Mutta ymmärsimme, että meillä on vain satoja "taistelupalvelimia", ja kaikki tämä "copy-paste" kehittäjien puolelta ei ole ollenkaan kätevää. Ymmärsimme, että meidän oli kerättävä se itse.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Yleensä on olemassa vakiomoduuli, joka voi kerätä tilastoja, mutta se on myös aktivoitava asetuksissa - tämä pg_stat_statements-moduuli. Mutta hän ei sopinut meille.

Ensinnäkin se määrittää samat kyselyt käyttämällä eri järjestelmiä samassa tietokannassa eri QueryID:t. Eli jos teet sen ensin SET search_path = '01'; SELECT * FROM user LIMIT 1;ja sitten SET search_path = '02'; ja sama pyyntö, silloin tämän moduulin tilastoissa on erilaisia ​​tietueita, enkä voi kerätä yleisiä tilastoja erityisesti tämän pyyntöprofiilin yhteydessä ottamatta huomioon järjestelmiä.

Toinen seikka, joka esti meitä käyttämästä sitä, on suunnitelmien puute. Eli ei ole suunnitelmaa, on vain itse pyyntö. Näemme, mikä hidastui, mutta emme ymmärrä miksi. Ja tässä palataan nopeasti muuttuvan tietojoukon ongelmaan.

Ja viimeinen hetki - "faktojen" puute. Toisin sanoen et voi käsitellä tiettyä kyselyn suoritustapausta – sellaista ei ole, on vain kootut tilastot. Vaikka tämän kanssa on mahdollista työskennellä, se on vain erittäin vaikeaa.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Siksi päätimme taistella copy-pastea vastaan ​​ja aloimme kirjoittaa kerääjä.

Keräilijä muodostaa yhteyden SSH:n kautta, muodostaa varmenteen avulla suojatun yhteyden palvelimeen tietokantaan ja tail -F "tarttuu" siihen lokitiedostossa. Joten tässä istunnossa saamme täydellisen "peilin" koko lokitiedostosta, jonka palvelin luo. Itse palvelimen kuormitus on minimaalinen, koska emme jäsennä siellä mitään, vain peilaamme liikennettä.

Koska olimme jo aloittaneet käyttöliittymän kirjoittamisen Node.js:ssä, jatkoimme kerääjän kirjoittamista siihen. Ja tämä tekniikka on oikeuttanut itsensä, koska on erittäin kätevää käyttää JavaScriptiä heikosti muotoillun tekstidatan, joka on loki, kanssa. Ja itse Node.js-infrastruktuuri taustaalustana mahdollistaa helpon ja kätevän työskentelyn verkkoyhteyksien ja jopa kaikkien tietovirtojen kanssa.

Vastaavasti "venytämme" kahta yhteyttä: ensimmäinen "kuuntelee" itse lokia ja ottaa sen itsellemme ja toinen kysyäkseen säännöllisesti tukiasemalta. "Mutta loki osoittaa, että merkki, jossa on id 123, on estetty", mutta tämä ei tarkoita mitään kehittäjälle, ja olisi mukavaa kysyä tietokannasta: "Mikä on OID = 123?" Ja siksi kysymme ajoittain tukiasemalta, mitä emme vielä tiedä itsestämme.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

"On vain yksi asia, jota et ottanut huomioon, on olemassa elefantin kaltaisia ​​mehiläisiä!..." Aloitimme tämän järjestelmän kehittämisen, kun halusimme valvoa 10 palvelinta. Ymmärryksessämme kriittisin, jossa ilmeni ongelmia, joita oli vaikea käsitellä. Mutta ensimmäisen vuosineljänneksen aikana saimme seurantaan sata - koska järjestelmä toimi, kaikki halusivat sitä, kaikilla oli mukavaa.

Kaikki tämä on laskettava yhteen, tietovirta on suuri ja aktiivinen. Itse asiassa käytämme sitä, mitä valvomme, mitä voimme käsitellä. Käytämme myös PostgreSQL:ää tietojen tallennusvälineenä. Ja mikään ei ole nopeampaa "kaataa" tietoja siihen kuin operaattori COPY Ei vielä.

Mutta pelkkä tietojen "kaataminen" ei ole tekniikkaamme. Koska jos sinulla on noin 50 100 pyyntöä sekunnissa sadalla palvelimella, tämä tuottaa 150-XNUMX Gt lokeja päivässä. Siksi meidän piti "leikata" alusta huolellisesti.

Ensinnäkin teimme osiointi päiväkohtaisesti, koska ketään ei yleensä kiinnosta päivien välinen korrelaatio. Mitä väliä sillä on, mitä sinulla oli eilen, jos tänä iltana julkaisit uuden version sovelluksesta - ja jo uusia tilastoja.

Toiseksi opimme (olimme pakotettuja) erittäin, erittäin nopea kirjoittaa käyttäen COPY. Eli ei vain COPYkoska hän on nopeampi kuin INSERT, ja vielä nopeammin.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Kolmas kohta - minun oli pakko hylkää liipaisimet ja vieraat avaimet. Toisin sanoen meillä ei ole lainkaan viitteellistä eheyttä. Koska jos sinulla on taulukko, jossa on pari FK:ta, ja sanot tietokantarakenteessa, että "tässä on lokitietue, johon FK viittaa esimerkiksi tietueiden ryhmään", niin kun lisäät sen, PostgreSQL ei ole muuta jäljellä kuin kuinka ottaa se ja tehdä se rehellisesti SELECT 1 FROM master_fk1_table WHERE ... sen tunnisteen kanssa, jota yrität lisätä - vain tarkistaaksesi, että tämä tietue on siellä, ettette "katkaise" tätä vierasavainta lisäykselläsi.

Yhden tietueen sijasta kohdetaulukkoon ja sen indekseihin saamme lisäedun, että luemme kaikki taulukot, joihin se viittaa. Mutta emme tarvitse tätä ollenkaan - tehtävämme on tallentaa mahdollisimman paljon ja mahdollisimman nopeasti mahdollisimman pienellä kuormalla. Joten FK - alas!

Seuraava kohta on yhdistäminen ja hajautus. Aluksi otimme ne käyttöön tietokantaan - onhan se kätevää heti, kun tietue saapuu, tehdä se jollain tabletilla "plus yksi" aivan laukaisimessa. No, se on kätevää, mutta sama huono asia - lisäät yhden tietueen, mutta sinun on pakko lukea ja kirjoittaa jotain muuta toisesta taulukosta. Lisäksi et vain lue ja kirjoitat, teet sen myös joka kerta.

Kuvittele nyt, että sinulla on taulukko, jossa lasket vain tietyn isännän kautta kulkeneiden pyyntöjen määrän: +1, +1, +1, ..., +1. Ja sinä et periaatteessa tarvitse tätä - kaikki on mahdollista summa keräilijän muistissa ja lähettää tietokantaan yhdellä kertaa +10.

Kyllä, joissain ongelmissa looginen eheytesi saattaa "hajota", mutta tämä on lähes epärealistinen tapaus - koska sinulla on normaali palvelin, siinä on akku ohjaimessa, sinulla on tapahtumaloki, loki tiedostojärjestelmä... Yleensä ei se ole sen arvoista. Tuottavuuden menetys, jonka saat laukaisimien/FK:n käynnistämisestä, ei ole aiheutumiesi kustannusten arvoinen.

Sama on hashoinnin kanssa. Tietty pyyntö lentää sinulle, lasket siitä tietyn tunnisteen tietokantaan, kirjoitat sen tietokantaan ja kerrot sen sitten kaikille. Kaikki on hyvin, kunnes nauhoitushetkellä luoksesi tulee toinen henkilö, joka haluaa nauhoittaa saman asian - ja sinut estetään, ja tämä on jo huono. Siksi, jos voit siirtää joidenkin tunnusten luomisen asiakkaalle (suhteessa tietokantaan), on parempi tehdä tämä.

Meille oli aivan täydellinen käyttää tekstistä MD5:tä - pyyntö, suunnitelma, malli,... Laskemme sen keräilijän puolella ja "kaadamme" valmiin tunnuksen tietokantaan. MD5:n pituus ja päivittäinen osiointi antavat meille mahdollisuuden olla huolehtimatta mahdollisista törmäyksistä.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Mutta jotta voimme tallentaa kaiken tämän nopeasti, meidän piti muuttaa itse tallennusmenettelyä.

Miten yleensä kirjoitat dataa? Meillä on jonkinlainen tietojoukko, jaamme sen useisiin taulukoihin ja sitten KOPIOimme sen - ensin ensimmäiseen, sitten toiseen, kolmanteen... Se on hankalaa, koska näytämme kirjoittavan yhtä tietovirtaa kolmessa vaiheessa peräkkäin. Epämiellyttävää. Voiko sen tehdä nopeammin? Voi!

Tätä varten riittää vain hajottaa nämä virrat rinnakkain. Osoittautuu, että meillä on virheitä, pyyntöjä, malleja, estoja, ... lentää erillisissä säikeissä - ja kirjoitamme sen kaiken rinnakkain. Riittää tähän pitää COPY-kanava jatkuvasti auki jokaiselle yksittäiselle kohdetaulukolle.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Eli keräilijällä siellä on aina virta, johon voin kirjoittaa tarvitsemani tiedot. Mutta jotta tietokanta näkee nämä tiedot, eikä joku juuttuisi odottamaan näiden tietojen kirjoittamista, KOPIOINTI on keskeytettävä tietyin väliajoin. Meille tehokkain jakso oli noin 100ms - suljemme sen ja avaamme sen heti uudelleen samaan pöytään. Ja jos meillä ei ole tarpeeksi yksi virtaus joidenkin huippujen aikana, teemme yhdistämisen tiettyyn rajaan asti.

Lisäksi havaitsimme, että tällaiselle kuormitusprofiilille mikä tahansa aggregointi, kun tietueita kerätään erissä, on pahaa. Klassinen paha on INSERT ... VALUES ja lisää 1000 levyä. Koska siinä vaiheessa sinulla on tallennushuippu, ja kaikki muut, jotka yrittävät kirjoittaa jotain levylle, odottavat.

Päästäksesi eroon tällaisista poikkeavuuksista, älä yksinkertaisesti yhdistä mitään, älä puskuroi ollenkaan. Ja jos puskurointia levylle tapahtuu (onneksi Node.js:n Stream API mahdollistaa sen) - lykkää tätä yhteyttä. Kun saat tapahtuman, että se on jälleen ilmainen, kirjoita siihen kertyneestä jonosta. Ja kun se on kiireinen, ota seuraava ilmainen altaalta ja kirjoita siihen.

Ennen kuin otimme käyttöön tämän lähestymistavan tietojen tallentamiseen, meillä oli noin 4K kirjoitusoperaatiot, ja tällä tavalla vähennimme kuormaa 4 kertaa. Nyt ne ovat kasvaneet vielä 6 kertaa uusien valvottujen tietokantojen ansiosta - jopa 100 MB/s. Ja nyt säilytämme lokit viimeisten 3 kuukauden ajalta noin 10-15 Tt:n tilavuudella toivoen, että vain kolmessa kuukaudessa kuka tahansa kehittäjä pystyy ratkaisemaan minkä tahansa ongelman.

Ymmärrämme ongelmat

Mutta vain kaiken tämän tiedon kerääminen on hyvää, hyödyllistä, merkityksellistä, mutta ei tarpeeksi - se on ymmärrettävä. Koska nämä ovat miljoonia erilaisia ​​suunnitelmia päivässä.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Mutta miljoonat ovat hallitsemattomia, meidän on ensin tehtävä "pienempi". Ja ensinnäkin sinun on päätettävä, kuinka järjestät tämän "pienen" asian.

Olemme tunnistaneet kolme keskeistä kohtaa:

  • joka lähetti tämän pyynnön
    Eli mistä sovelluksesta se "saapui": verkkoliittymästä, taustajärjestelmästä, maksujärjestelmästä vai jostain muusta.
  • missä se tapahtui
    Millä tietyllä palvelimella? Koska jos sinulla on useita palvelimia yhden sovelluksen alla, ja yhtäkkiä yksi "menee tyhmäksi" (koska "levy on mätä", "muisti vuotanut", jokin muu ongelma), sinun on osoitettava erityisesti palvelin.
  • как ongelma ilmeni tavalla tai toisella

Ymmärtääksemme "kuka" lähetti meille pyynnön, käytämme vakiotyökalua - istuntomuuttujan asettaminen: SET application_name = '{bl-host}:{bl-method}'; — lähetämme sen bisneslogiikkapalvelimen nimen, jolta pyyntö tulee, sekä sen käynnistäneen menetelmän tai sovelluksen nimen.

Kun olemme välittäneet pyynnön "omistajan", se on tulostettava lokiin - tätä varten määritämme muuttujan log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Kiinnostuneille ehkä katso ohjekirjastamitä se kaikki tarkoittaa. Osoittautuu, että näemme lokissa:

  • aika
  • prosessi- ja tapahtumatunnisteet
  • tietokannan nimi
  • Tämän pyynnön lähettäneen henkilön IP-osoite
  • ja menetelmän nimi

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Sitten tajusimme, että ei ole kovin mielenkiintoista tarkastella yhden pyynnön korrelaatiota eri palvelimien välillä. Harvoin joudut tilanteeseen, jossa yksi sovellus päihittää tasaisesti siellä täällä. Mutta vaikka se olisi sama, katso mitä tahansa näistä palvelimista.

Joten tässä on leikkaus "yksi palvelin - yksi päivä" se osoittautui meille riittäväksi mihin tahansa analyysiin.

Ensimmäinen analyyttinen osa on sama "näyte" - lyhennetty suunnitelman esitysmuoto, josta on poistettu kaikki numeeriset indikaattorit. Toinen leikkaus on sovellus tai menetelmä, ja kolmas leikkaus on tietty suunnitelmasolmu, joka aiheutti meille ongelmia.

Kun siirryimme tietyistä tapauksista malleihin, saimme kaksi etua kerralla:

  • analyysiobjektien lukumäärän moninkertainen vähentäminen
    Meidän ei tarvitse analysoida ongelmaa enää tuhansien kyselyiden tai suunnitelmien, vaan kymmenien mallien avulla.
  • aikajanalla
    Toisin sanoen tiivistämällä "faktoja" tietyssä osiossa voit näyttää niiden ulkonäön päivän aikana. Ja tässä voit ymmärtää, että jos sinulla on jonkinlainen kaava, joka tapahtuu esimerkiksi kerran tunnissa, mutta sen pitäisi tapahtua kerran päivässä, sinun on mietittävä mikä meni pieleen - kuka sen aiheutti ja miksi, ehkä sen pitäisi olla täällä ei pitäisi. Tämä on toinen ei-numeerinen, puhtaasti visuaalinen analyysimenetelmä.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Loput menetelmät perustuvat indikaattoreihin, jotka poimimme suunnitelmasta: kuinka monta kertaa tällainen kuvio esiintyi, kokonais- ja keskimääräinen aika, kuinka paljon tietoa luettiin levyltä ja kuinka paljon muistista...

Koska esimerkiksi tulet isännän analytiikkasivulle, katso - jotain alkaa lukea liikaa levyltä. Palvelimella oleva levy ei kestä sitä - kuka lukee sitä?

Ja voit lajitella minkä tahansa sarakkeen mukaan ja päättää, mitä käsittelet juuri nyt - prosessorin tai levyn kuormitus vai pyyntöjen kokonaismäärä... Lajittelimme sen, katsoimme "ylimpiä" ja korjasimme sen ja julkaisi uuden version sovelluksesta.
[videoluento]

Ja heti voit nähdä erilaisia ​​sovelluksia, jotka tulevat saman mallin kanssa pyynnöstä, kuten SELECT * FROM users WHERE login = 'Vasya'. Käyttöliittymä, taustajärjestelmä, käsittely... Ja ihmettelet, miksi käsittely lukisi käyttäjän, jos hän ei ole vuorovaikutuksessa hänen kanssaan.

Päinvastainen tapa on nähdä heti sovelluksesta, mitä se tekee. Esimerkiksi käyttöliittymä on tämä, tämä, tämä ja tämä kerran tunnissa (aikajana auttaa). Ja heti herää kysymys: näyttää siltä, ​​ettei käyttöliittymän tehtävä ole tehdä jotain kerran tunnissa...

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Jonkin ajan kuluttua huomasimme, että meiltä puuttui yhteenveto tilastot suunnitelman solmujen mukaan. Eristimme suunnitelmista vain ne solmut, jotka tekevät jotain itse taulukoiden tiedoilla (lukevat/kirjoittavat niitä indeksillä tai eivät). Itse asiassa vain yksi näkökohta on lisätty edelliseen kuvaan - kuinka monta tietuetta tämä solmu toi meille?, ja kuinka monta hylättiin (suodattimen poistamat rivit).

Sinulla ei ole sopivaa indeksiä levyllä, teet sille pyynnön, se lentää indeksin ohi, putoaa Seq Scaniin... olet suodattanut pois kaikki tietueet yhtä lukuun ottamatta. Miksi tarvitset 100 miljoonaa suodatettua tietuetta päivässä? Eikö olekin parempi kerätä indeksi yhteen?

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Analysoituamme kaikki suunnitelmat solmukohtaisesti huomasimme, että suunnitelmissa on tyypillisiä rakenteita, jotka näyttävät erittäin todennäköisesti epäilyttävältä. Ja olisi mukavaa kertoa kehittäjälle: "Ystävä, täällä luet ensin indeksin mukaan, lajittelet ja sitten leikkaat" - yleensä on yksi tietue.

Jokainen kyselyitä kirjoittanut on luultavasti törmännyt tähän malliin: "Anna minulle viimeinen Vasyan tilaus, sen päivämäärä." Ja jos sinulla ei ole päivämäärän mukaista hakemistoa tai käyttämässäsi hakemistossa ei ole päivämäärää, astu täsmälleen samalle "haravalle" .

Mutta tiedämme, että tämä on "rake" - joten miksi et kertoisi heti kehittäjälle, mitä hänen pitäisi tehdä. Vastaavasti nyt avattaessa suunnitelmaa kehittäjämme näkee heti kauniin kuvan vinkeineen, jossa hänelle heti sanotaan: "Sinulla on ongelmia siellä täällä, mutta ne ratkaistaan ​​näin ja tuolla tavalla."

Tämän seurauksena ongelmien ratkaisemiseen tarvittava kokemus alussa ja nyt on laskenut merkittävästi. Tällainen työkalu meillä on.

PostgreSQL-kyselyiden joukkooptimointi. Kirill Borovikov (Tensori)

Lähde: will.com

Lisää kommentti