PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Ettekandes tutvustatakse mÔningaid lÀhenemisviise, mis vÔimaldavad jÀlgida SQL-pÀringute jÔudlust, kui neid on miljoneid pÀevas, ja hallatavaid PostgreSQL servereid on sadu.

Millised tehnilised lahendused vÔimaldavad meil tÔhusalt sellist teabehulka töödelda ja kuidas see lihtsustab tavalise arendaja elu.

Vaata videot

Kellele on huvitav konkreetsete probleemide analĂŒĂŒs ning erinevad optimeerimisetehnikad SQL-pĂ€ringute ja tĂŒĂŒpiliste DBA-ĂŒlesannete lahendamine PostgreSQL-is — saate samuti tutvuda artiklite seeriaga sel teemal.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)
Minu nimi on Kirill Borovikov, ma esindan ettevÔtet "Tensor". Konkreetsemalt spetsialiseerun ma meie ettevÔttes andmebaasidega töötamisele.

TĂ€na rÀÀgin teile, kuidas tegeleme pĂ€ringute optimeerimisega, kui peate mitte "kaevama" ĂŒhe pĂ€ringu tootlikkuse kallal, vaid lahendama probleemi massiliselt. Kui pĂ€ringute arv on miljonite kaupa ja peate leidma mingid lĂ€henemisviisid selle suure probleemi lahendamiseks. selle suure probleemi.

Üldiselt on "Tensor" miljoni meie kliendi jaoks Sbis — meie rakendus: ettevĂ”tte sotsiaalne vĂ”rk, lahendused videokĂ”nede jaoks, sisemise ja vĂ€lise dokumenteerimise jaoks, raamatupidamise ja laosĂŒsteemide haldamine
 See on nagu „megakombain“ Ă€ri terviklikuks juhtimiseks, milles on rohkem kui 100 erinevat sisemist projekti.

Selleks, et kĂ”ik need sujuksid ja areneksid — meil on 10 arenduskeskust ĂŒle kogu riigi, kus on rohkem 1000 arendajat.

PostgreSQL-iga töötame alates 2008. aastast ja oleme kogunud suure hulga andmeid — need on kliendiandmed, statistilised, analĂŒĂŒtilised, andmed vĂ€listest infosĂŒsteemidest — ĂŒle 400TB.Ainult „töös“ on umbes 250 serverit, aga kokku jĂ€lgime andmebaasi servereid, neid on umbes 1000.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

SQL on deklaratiivne keel. Esiteks kirjeldate, mitte „kuidas“, vaid „mida“ soovite saada. DBMS teadub paremini, kuidas teha JOIN – kuidas ĂŒhendada tabelid, millised tingimused kehtestada, mis lĂ€heb indeksi kaudu ja mis mitte


MĂ”ned andmebaasid vĂ”tavad vastu vihjeid: „Ei, ĂŒhenda need kaks tabelit sellises jĂ€rjekorras“, kuid PostgreSQL ei oska seda teha. See on teadlik seisukoht juhtivate arendajate poolt: „Eelistame optimeerijat tĂ€iustada, kui lubada arendajatel mingite vihjete abil töötada.“

Kuid hoolimata sellest, et PostgreSQL ei luba „vĂ€ljast“ ennast juhtida, vĂ”imaldab see suurepĂ€raselt nĂ€hes, mis toimub selle „sises“, kui teete oma pĂ€ringu ja kus tekivad probleemid.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Tavaliselt, milliste klassikaliste probleemidega tuleb arendaja [DBA] juurde? „Me protsessisime pĂ€ringu ja meil on kĂ”ik aeglane, kĂ”ik hangub, midagi toimub
 See on kahtlane!“

PÔhjused on peaaegu alati samad:

  • tĂ”hus algoritm pĂ€ringule
    Arendaja: „Praegu ĂŒhendan SQL-is 10 tabelit JOIN-iga
“ – ja ootab, et tema tingimused imekombel efektiivselt „lahendatakse“ ja ta saab kĂ”ik kiiresti. Kuid imesid ei juhtu, ja iga sĂŒsteem sellise varieerimise puhul (10 tabelit ĂŒhe FROM-i sees) annab alati mingi ebatĂ€psuse.artikkel]
  • vakku statistikat
    Moment on vĂ€ga oluline just PostgreSQL jaoks, kui olete suure andmehulgaga serverisse „sisenenud“, teete pĂ€ringu — ja see „seksib“ tabelis. Sest eile oli seal 10 kirjet, aga tĂ€na 10 miljonit, kuid PostgreSQL ei tea sellest veel, ja meil tuleb talle seda nĂ€idata.artikkel]
  • ressursside „ummistus“
    Olete pannud suure ja koormatud andmebaasi nĂ”rkadele serveritele, kellel puuduvad ketta, mĂ€lude ja protsessori vĂ”imekus. Ja kĂ”ik... Kusagil on olemas jĂ”udluse lagi, mille ĂŒletamiseks te enam ei suuda.
  • blokaadid
    TĂ€pne moment, kuid need on kĂ”ige asjakohasemad erinevate muudetud pĂ€ringute (INSERT, UPDATE, DELETE) jaoks — see on eraldi suur teema.

Kava saamine


 Ja kÔigi teiste jaoks me vajame kava! Me peame nÀgema, mis serveris toimub.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

PĂ€ringu tĂ€itmise plaan PostgreSQL jaoks on pĂ€ringu tĂ€itmise algoritmi puu tekstivĂ€lja kujul. Just see algoritm, mille analĂŒĂŒsis plaanija on tunnistanud kĂ”ige efektiivsemaks.

Iga puu sĂ”lm — operatsioon: andmete vĂ€ljavĂ”tmine tabelist vĂ”i indeksist, bitikaardi koostamine, kahe tabeli ĂŒhendamine, liitmine, ristamine vĂ”i valikute eristamine. PĂ€ringu tĂ€itmine — lĂ€bimine selle puu sĂ”lmedest.

KĂŒsimuse plaani saamiseks on kĂ”ige lihtsam viis kasutada kĂ€sku EXPLAIN. Selleks, et saada kĂ”iki reaalseid atribuute, st tegelikult pĂ€ringut andmebaasis tĂ€ita — EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Halb koht: kui te seda tĂ€idate, toimub see „siin ja praegu”, seega sobib see ainult kohaliku tĂ”rkeotsingu jaoks. Kui aga vĂ”tate mĂ”ne kĂ”rge koormusega serveri, mis seisab tugeva andmevoo all, ja nĂ€ete: „Ai! Siin me olime aeglaselt tĂ€itnudpĂ€ringu.” Pool tundi, tund tagasi — kui te jooksisite ja tĂ”ite selle pĂ€ringu logidest, kandsite selle uuesti serverisse, on kogu teie andmekogum ja statistika muutunud. Te tĂ€idate selle tĂ”rkeotsinguks — ja see tĂ€itub kiiresti! Ja te ei saa aru, „miks”, miks pĂ€ringu.» Pool tundi, tund aega tagasi — samal ajal, kui te jooksisite ja tĂ”ite selle pĂ€ringu logidest, toite selle jĂ€lle serverisse, oli kogu teie andmestik ja statistika muutunud. Te tĂ€idate selle, et siluda — ja see tĂ€idetakse kiiresti! Ja te ei saa aru, "miks", miks oli aeglaselt.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Kuna mÔista, mis juhtus just sel hetkel, kui pÀring serveris tÀidetakse, on tarkade inimeste poolt kirjutatud moodul auto_explain. See on praktiliselt kÔikides levinumates PostgreSQL'i distributsioonides ja seda saab lihtsalt konfig-failis aktiivseks teha.

Kui ta mÔistab, et mÔni pÀring kestab kauem, kui olete talle öelnud, siis ta teeb selle pÀringu 'vÔtte' plaani ja kirjutab need koos logisse..

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

NĂ€ib, et kĂ”ik on nĂŒĂŒd hĂ€sti, lĂ€heme logisse ja nĂ€eme seal
 [ĐżĐŸŃ€Ń‚ŃĐœĐșа тДĐșста]. Aga me ei saa sellest midagi öelda, peale selle, et see on suurepĂ€rane plaan, kuna see kestis 11 ms.

Kohati nĂ€ib, et kĂ”ik on korras — aga ei ole selge, mis tegelikult toimus. VĂ€lja arvatud ĂŒldine aeg, ei nĂ€e me eriti midagi. Sest vaadata sellise 'latuka' plain text'i on ĂŒldiselt ebaĂŒlevaatlik.

Kuid isegi kui see on ebamugav, on palju tÔsisemaid probleeme:

  • SĂ”lmes nĂ€idatakse resursside summa kogu alampuu tema all. See tĂ€hendab, et lihtsalt ei saa teada, kui palju aega on konkreetselt selles Index Scan'is kulutatud — kui seal all on mingi sisse seatud tingimus. Peame dĂŒnaamiliselt vaatama, kas seal on 'lapsed' ja tingimuslikud muutujad, CTE — ja kĂ”ik see 'meeles' vĂ€lja arvutama.
  • Teine punkt: sĂ”lmes nĂ€idatud aeg on sĂ”lme tĂ€itmise aeg. Kui see sĂ”lm tĂ€ideti nĂ€iteks tabeli kirjade tsĂŒkli kĂ€igus mitu korda, suureneb plaanis loops — selle sĂ”lme tsĂŒklite arv. Kuid aatomite tĂ€itmise aeg jÀÀb plaanis endiseks. Seega, et mĂ”ista, kui palju see sĂ”lm kokkuvĂ”ttes tĂ€ideti, tuleb ĂŒhte teisega korrutada — taas “meeles”.

Sellistes olukordades on «Kes on kĂ”ige nĂ”rgem lĂŒli?» mĂ”istmine praktiliselt vĂ”imatu. SeetĂ”ttu kirjutavad isegi arendajad «manuaalis», et «Plani mĂ”istmine on kunst, mida tuleb Ă”ppida, kogemus ».

Aga meil on 1000 arendajat ja ei saa seda kogemust igale pead anda. Mina, sina, tema — teavad, aga see, kes seal, ei pruugi. VĂ”ib-olla ta Ă”pib, aga vĂ”ib-olla ei Ă”pi, kuid talle peab juba praegu tööle minema — kust tal siis see kogemus vĂ”tta on.

Plani visualiseerimine

SeetĂ”ttu mĂ”istsime — et nende probleemidega tegeleda, on meil vaja head plaani visualiseerimist. [artikel]

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Me lĂ€ksime kĂ”igepealt «turule» — otsime internetist, mida ĂŒldse olemas on.

Kuid selgus, et suhteliselt "elavaid" lahendusi, mis enam-vĂ€hem arenevad, on vĂ€ga vĂ€he — sĂ”na otseses mĂ”ttes ĂŒks: explain.depesz.com Hubert Lubaczewski poolt. Sisestades tekstilise esitluse plaanist, kuvatakse sulle tabel analĂŒĂŒsitud andmetega:

  • sĂ”lme enda tööaeg
  • koguaeg kogu allpuu kohta
  • salvestuste arv, mis on vĂ€lja tĂ”mmatud ja mis statistiliselt oodati
  • sĂ”lme enda keha

Sellel teenusel on ka vĂ”imalus jagada lingiarhiivi. Sa viskad sinna oma plaani ja ĂŒtled: „Hei, Vasja, siin on link, seal on midagi valesti.“

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Aga vÀiksed probleemid on ka.

Esiteks, tohutu hulk „kopeerimist ja kleepimist“. Sa vĂ”tad logsakti ja paned selle sinna, ja jĂ€lle, ja jĂ€lle.

Teiseks, andmete lugemise arvu analĂŒĂŒsi puudumine — just need buffers, mida ta vĂ€lja toob EXPLAIN (ANALYZE, BUFFERS), siin me ei nĂ€e. Ta lihtsalt ei oska neid analĂŒĂŒsida, neid mĂ”ista ja nendega töötada. Kui loed palju andmeid ja mĂ”istad, et vĂ”id kettale ja mĂ€lu vahemĂ€lusse vale jaotuse teha, on see teave vĂ€ga oluline.

Kolmas negatiivne punkt on vÀga nÔrk selle projekti areng. Commit'id on vÀga vÀiksed, hÀsti kui kord poole aasta jooksul, ja kood on Perl'is.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Aga see on kĂ”ik 'lĂŒĂŒrika', sellega oleks vĂ”imalik kuidagi elada, kuid on ĂŒks asi, mis meid sellest teenusest tugevasti eemale tĂ”ukab. Need on Common Table Expression (CTE) ja erinevate dĂŒnaamiliste sĂ”lmede, nagu InitPlan/SubPlan, analĂŒsi vead.

Kui uskuda seda pilti, siis on iga eraldi sĂ”lme tĂ€itmise koguaeg suurem kui kogu pĂ€ringu tĂ€itmise aeg. KĂ”ik on lihtne — CTE Scan'i sĂ”lmest ei ole maha arvestatud selle CTE genereerimise aega.. Seega ei tea me enam Ă”iget vastust, kui kaua kestis CTE skaneerimine.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Siit mĂ”istsime, et on aeg kirjutada oma lahendus — hurraa! Iga arendaja ĂŒtleb: 'NĂŒĂŒd me kirjutame oma, super lihtne on!'

VĂ”tsime veebiteenustele tĂŒĂŒpilise tehnoloogia: sĂŒda Node.js + Express, Bootstrap ja ilusate diagrammide jaoks — D3.js. Ja meie ootused said pĂ”hjalikult tĂ€idetud — saime esimese prototĂŒĂŒbi kahe nĂ€dalaga:

  • oma plaani parser
    See tĂ€hendab, et nĂŒĂŒd suudame analĂŒĂŒsida igasuguseid plaane, mida PostgreSQL genereerib.
  • korrektne dĂŒnaamiliste sĂ”lmede analĂŒĂŒs — CTE Scan, InitPlan, SubPlan
  • buffersi jaotuse analĂŒĂŒs — kus andmelehed loetakse mĂ€lust, kus kohalikust vahemĂ€lust, kus kettalt
  • oleme saanud selguse
    Kuna ei pea kĂ”ike seda logis "kaevama", vaid nĂ€gema "nĂ”rgimat lĂŒli" kohe pildilt.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Oleme saanud umbes sellise pildi — kohe sĂŒntaksihighlighting'iga. Kuid tavaliselt töötavad meie arendajad juba mitte tĂ€ieliku plaaniga, vaid millegagi lĂŒhemaga. KĂ”ik numbrid oleme juba parsitud ja ĂŒhte vĂ”i teise suunda visatud, alles on ainult esimene rida, mis see sĂ”lm on: CTE Scan, CTE loomine vĂ”i Seq Scan mĂ”ne tabeli jĂ€rgi.

Seda lĂŒhendatud esitust nimetameme plaanimalliks.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Mis veel oleks mugav? Mugav oleks nĂ€ha, kui suur osa aega millisele sĂ”lmele jaguneb — ja lihtsalt "kleepisime" kĂŒljele kĂŒpsisetabel.

Suuname sĂ”lmele ja nĂ€eme — Ă€ratus! Seq Scan vĂ”ttis kogu ajast vĂ€hem kui veerandi, aga ĂŒlejÀÀnud 3/4 vĂ”ttis CTE Scan. Kohutav! See on vĂ€ike mĂ€rk "kiirusest" CTE Scan'i puhul, kui kasutate neid aktiivselt oma pĂ€ringutes. Nad ei ole vĂ€ga kiired — nad kaotavad isegi tavapĂ€rase tabeli skaneerimisele. [artikel] [artikel]

Kuid tavaliselt on sellised diagrammid huvitavamad ja keerukamad, kui suuname kohe segmendile ja nĂ€eme nĂ€iteks, et ĂŒle poole kogu ajast on lĂ€inud mĂ”ne Seq Scan 'förstinud'. Ning seal sees oli mingisugune Filter, mille tĂ”ttu visati palju kirjeid kĂ”rvale... Selle pildi saab otse arendajale saata ja öelda: 'Vassil, sul on siin tĂ”eliselt kehvasti! Lahenda see ĂŒles, vaata — midagi on valesti!'

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Muidugi ei kÀinud see ilma 'kombeideta'.

Esimene probleem, millele 'astepidi' astusime, oli ĂŒmardamise probleem. Iga eraldi sĂ”lme aeg plaanis on nĂ€idatud tĂ€psusega kuni 1”s. Ja kui sĂ”lme tsĂŒklite arv ĂŒletab nĂ€iteks 1000 — pĂ€rast PostgreSQL'i tĂ€itmist jagas 'tĂ€psusega', siis tagasi arvutades saame kokkuaja 'kusagil 0,95ms ja 1,05ms vahel'. Kui arvestus kĂ€ib mikrosekundite lĂ”ikes, on see veel talutav, aga kui juba [miljoni] sekundite osas — tuleb sĂ”lmede plaanis 'kes kui palju tarbis' neid andmeid arvesse vĂ”tta.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Teine, keerulisem aspekt on ressursside (neid samu puhvrid) jaotamine dĂŒnaamiliste sĂ”lmede vahel. See maksis meile prototĂŒĂŒbi esimese kahe nĂ€dala kĂ”rval veel neli nĂ€dalat.

Selline probleem on ĂŒsna kerge tekkima — loome CTE ja seal nĂ€iliselt midagi loeme. Tegelikult on PostgreSQL „nutikas” ja ei loe seal otse midagi. Siis vĂ”tame sellest esimesest kirjest, ja sellele — sadandeist samast CTE-st.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Vaadates plaani, saame aru — kummaline, meil oli 3 bufferit (andmelehti), mis olid „tarbitud” Seq Scan'is, veel 1 CTE Scan'is ja veel 2 teises CTE Scan'is. Kui kĂ”ik kokku liita, saada 6, aga tabelist lugesime ainult 3! CTE Scan ei loe mitte kuskilt, vaid töötab otse protsessi mĂ€luga. Siin on selgelt midagi valesti!

Tegelikult selgub, et need 3 andmelehte, mida kĂŒsiti Seq Scan'ilt, kĂŒsis esmalt 1. CTE Scan ja seejĂ€rel 2., mis luges juurde veel 2. Seega loeti kokku 3 andmelehte, mitte 6.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Ja see pilt viis meid arusaamisele, et plaani tĂ€itmine ei ole enam puu, vaid lihtsalt mingi suuline graaf. Ja meil on tekkinud umbes selline diagramm, et mĂ”ista, "mida-kusagilt ĂŒldse tuli". See tĂ€hendab, et siin me lĂ”ime CTE pg_class'ist ja palusime seda kaks korda ning praktiliselt kogu meie aeg kulus teisel korral, kui me seda kĂŒsisime. On selge, et 101. kirje lugemine on oluliselt kallim kui lihtsalt 1. tabelist.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Me hingasime hetkeks kergendatult. Ütlesime: "NĂŒĂŒd, Neo, sa tead kung fu! NĂŒĂŒd on meie kogemus otse sinu ekraanil. NĂŒĂŒd saad seda kasutada." [artikel]

Logide konsolideerimine

Meie 1000 arendajat hingasid kergendatult. Aga me mÔistsime, et meil on vaid sadu "laevade" servereid ja see "kopeerimine-kleepimine" arendajatelt on vÀgagi ebamugav. Saime aru, et peame selle ise kokku koguma.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Tegelikult on olemas ametlik moodul, mis oskab statistikat koguda, aga seda tuleb samuti konfigureerida — see on moodul pg_stat_statements. Kuid see ei sobinud meile.

Esiteks mÀÀrab ta sama pĂ€ringu erinevatele skeemidele ĂŒhe ja sama andmebaasi raames erinevad QueryId. See tĂ€hendab, et kĂ”igepealt tehes SETEERIGE otsingutee = '01'; VALIGE * KASUTAJAST PIIR 1;, ja siis SETEERIGE otsingutee = '02'; ja sama pĂ€ringuga on selle mooduli statistikas erinevad kirjed ning ma ei suuda koguda ĂŒldist statistikat just selle pĂ€ringu profiili lĂ”ikes, arvestamata skeemi.

Teine aspekt, mis takistas meil selle kasutamist — plaanside puudumine. See tĂ€hendab, et plaani ei ole, on ainult pĂ€ring ise. Me nĂ€eme, mis takistas, kuid ei saa aru, miks. Ja siin tuleme tagasi kiiresti muutuva andmestiku probleemile.

Ja viimane punkt — puudumine «faktidest». See tĂ€hendab, et ei saa viidata konkreetsele pĂ€ringu tĂ€itmise instantsile — seda ei ole, on ainult agregaatstatistika. Sellega on vĂ”imalik töötada, kuid see on vĂ€ga keeruline.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

SeetÔttu otsustasime «kopeerimise ja kleepimisega» vÔidelda ja hakkasime kirjutama kollektor.

Kollektor ĂŒhendub SSH kaudu, loob turvalise ĂŒhenduse sertifikaadiga andmebaasiserveriga ja tail -F ĂŒhendub sellega logifailiga. Sel moel saame kogu logifaili tĂ€ieliku «peegelpildi», mille genereerib server. Serveri koormus on selle juures minimaalne, sest me ei parsi seal midagi, lihtsalt peegeldame liiklust.

Kuna me oleme juba alustanud liidese loomist Node.js-is, siis jĂ€tkasime kollektorit arendamist samuti seal. See tehnoloogia on end Ă”igustanud, kuna halvasti vormindatud tekstiliste andmete, nagu logide, töötlemiseks on JavaScripti kasutamine vĂ€ga mugav. Ja Node.jsi infrastruktuur tagab, et on lihtne ja mugav töötada vĂ”rguĂŒhenduste ja igasuguste andmevoogudega.

Seega „venitame” kaks ĂŒhendust: esimene, et „kuulata” logi ja selle enda juurde korjata, ning teine — et perioodiliselt andmebaasilt kĂŒsida. „Logis tuli teade, et tabel oid-iga 123 on blokeeritud”, kuid see ei ĂŒtle arendajale midagi, seega oleks hea kĂŒsida andmebaasilt: „Mis asi on OID = 123?” Nii me siis perioodiliselt kĂŒsime andmebaasilt neid asju, mida me veel ei tea.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

«Sa ainult ĂŒhte asja ei arvutanud, on olemas elevandilaadseid mesilasi!..» Me alustasime selle sĂŒsteemi vĂ€ljatöötamist, kui soovisime jĂ€lgida 10 serverit. Need olid meie arvates kĂ”ige kritilisemad, kus esines probleeme, millega oli keeruline tegeleda. Kuid juba esimese kvartali jooksul saime jĂ€lgimise alla sada — sest sĂŒsteem «sisenes», kĂ”ik soovisid, kĂ”igile oli mugav.

Kogu see info tuleb kokku panna, andmevoog on suur ja aktiivne. Tegelikult jĂ€lgime seda, milles oskame toime tulla — seda ka kasutame. Kasutame andmete laona ka PostgreSQL-i. Ja ei ole mingit kiiremat viisi, et sinna andmeid «pudendada», kui operaator. COPY pole veel.

Aga lihtsalt andmete «pudendamine» — see ei ole pĂ€ris meie tehnoloogia. Sest kui teil on sajale serverile kokku umbes 50k pĂ€ringut sekundis, genereerib see teile 100-150GB logisid pĂ€evas. SeetĂ”ttu pidime andmebaasi ettevaatlikult «lĂ”ikama».

Esiteks tegime jaotamise pĂ€evade kaupa, sest pĂ”himĂ”tteliselt ei huvitunud kedagi ööpĂ€evade vahelisest korrelatsioonist. Mis vahet seal on, mis sul eile oli, kui sa tĂ€na öösel uue rakenduse versiooni vĂ€ljalaskmine — ja juba on mingi uus statistika.

Teiseks, me Ôppisime (olime sunnitud) vÀga-vÀga kiiresti kirjutama kasutades COPY. See tÀhendab, et mitte lihtsalt COPY, kuna see on kiirem kui INSERT, aga veel kiiremini.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Kolmas punkt — pidime loobuma triggereist ning samuti Foreign Key'dest. See tĂ€hendab, et meil puudub tĂ€iesti viidatud terviklikkus. Sest kui teil on tabel, millel on paar FK-d, ja te ĂŒtlete andmebaasi struktuuris, et "siin on logis olemas viide nĂ€iteks grupile kirjeid", siis kui te selle sisestate, ei jÀÀ PostgreSQL-l midagi muud ĂŒle, kui lihtsalt tĂ€ita SELECT 1 FROM master_fk1_table WHERE ... selle identifikaatoriga, mida te pĂŒĂŒate sisestada — lihtsalt selleks, et kontrollida, et see kirje seal olemas on, et te ei "purusta" oma sisestusega seda Foreign Key'd.

Saame sihtotstarbelisse tabelisse ĂŒhe kirje asemel ja selle indeksid, lisaks lugemist kĂ”igilt tabelitelt, millele see viitab. Ja me ei vaja seda ĂŒldse — meie ĂŒlesanne on kirjutada nii palju kui vĂ”imalik ja nii kiiresti kui vĂ”imalik vĂ€iksema koormusega. Nii et FK — vĂ€lja!

JĂ€rgmine punkt on agregatsioon ja hĂ€sheering. Alguses rakendasime neid andmebaasis — see on mugav, kui igasugune kirje saabub, teha midagi mĂ”nes tabelis. "pluss ĂŒks" otse triggeris.. HĂ€sti, mugav, aga halb samas — teete ĂŒhe kirje lisamise, aga peate lugema ja kirjutama veel midagi teisest tabelist. Ja mis veel hullem, tuleb lugeda ja kirjutada iga kord.

Ja nĂŒĂŒd kujutage ette, et teil on tabel, kus te lihtsalt loete konkreetse hosti kaudu lĂ€bitud pĂ€ringute arvu: +1, +1, +1, ..., +1. Ja see pole teile tegelikult vajalik — kĂ”ik vĂ”ib mĂ€lu summaarse koguse jĂ€rgi kollektoris ja saata andmebaasi korraga. +10.

Jah, teil vĂ”ib tekkida „purunemine“ loogilises terviklikkuses, kuid see on praktiliselt reaalne juhtum — sest teil on normaalne server, sellel on patarei kontrolleris, teil on tehingute ajalugu, ajakiri failisĂŒsteemis... ÜhesĂ”naga, see ei ole seda vÀÀrt. Kaotatud jĂ”udluse hind ei ole Ă”igustatud, mida te saate triggerite/FK-de tĂ”ttu, need kulud, mida te sellega kaasnevalt kannate.

Sama kehtib ka hĂ”lmitud rĂ€mpspostide kohta. Teile tuleb mingi pĂ€ring, millest arvutate andmebaasis mingi identifikaatori, kirjutate selle andmebaasi ja rÀÀgite kĂ”igile sellest. KĂ”ik on hĂ€sti, kuni salvestamise hetkel ei tule teise soovi salvestada sama — ja teil tekib lukustus, mis on juba halb. Seega, kui saate teatud ID-de genereerimise kliendisse viia (andmebaasi suhtes), on parem see teha.

Meile sobis suurepĂ€raselt kasutada teksti MD5 — pĂ€ringu, plaane, mallide jne. Me arvutame selle kogujapoolsel kĂŒljel ja „töötame” andmebaasi juba valmistatud ID-ks. MD5 pikkus ja pĂ€evaste osade muutmine vĂ”imaldavad meil mitte muretseda vĂ”imalike kokkulangevuste pĂ€rast.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Kuid et seda kÔike kiiresti salvestada, pidime modifitseerima salvestamisprotseduuri.

Kuidas andmeid tavaliselt kirjutatakse? Meil on mingi andmestik, jaotame selle mitmeks tabeliks ning siis COPY — kĂ”igepealt esimesse, siis teise, kolmandasse
 Ebamugav, sest nĂ€iliselt kirjutame ĂŒhe andmevoo kolmes etapis jĂ€rjestikku. Ebameeldiv. Kas seda saab kiiremini teha? Saab!

Selleks piisab, kui panna need voolud ĂŒksteise kĂ”rval параллДлne. Tulemusena lendavad meil eraldi voogudes vead, pĂ€ringud, mallid, lukustused,
 — ja me kirjutame seda kĂ”ike paralleelselt. Selleks piisab, hoida pidevalt avatud COPY-kanalit iga eraldi sihtmatsi jaoks..

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

See tĂ€hendab, et kollektsionĂ€ril on alati voog, kuhu ma saan kirjutada vajalikud andmed. Kuid et baas need andmed nĂ€eks, ja keegi ei jÀÀks lukustusse ootes, kuni need andmed kirjutatakse, COPY tuleb katkestada teatud regulaarsusega. Meie jaoks tĂ”estuslikult efektiivne aeg oli umbes 100 ms — sulgeme ja avame kohe uuesti sama tabeli. Ja kui meil ei piisa mingist voogust teatud haripunktides, siis teeme tĂ”mbamise kindla piiri ulatuses.

Lisaks oleme vĂ€lja selgitanud, et sellise koormusprofiili puhul on iga agregatsioon, kui kirjed kogutakse paketti — see on kuri. Klassikaline kuri on INSERT ... VALUES ja edasi 1000 kirjet. Sest sel hetkel tekkib teil salvestamisel tipptunne, ja kĂ”ik teised, kes ĂŒritavad midagi kettale kirjutada, peavad ootama.

Ette vĂ€ltimiseks sellistest anomaaliatest, Ă€rge aggregeerige midagi, Ă€rge bufferdage ĂŒldse. Ja kui siiski tekib kirjutamise vahemĂ€lu (Ă”nneks vĂ”imaldab Node.js Stream API seda tuvastada) — lĂŒkake see ĂŒhendus edasi. Just siis, kui saate teate, et see on jĂ€lle vaba — kirjutage sinna akumuleeritud jĂ€rjekorrast. Kui see on hĂ”ivatud — vĂ”tke basseinist jĂ€rgmine vaba ja kirjutage sinna.

Enne sellise andmete salvestamise lĂ€henemise kasutuselevĂ”ttu oli meil umbes 4K kirjutamisoperatsiooni, kuid sellise lĂ€henemisega vĂ€hendasime koormust neljakordselt. NĂŒĂŒd on see kasvanud veel kuus korda tĂ€nu uutele jĂ€lgitavatele andmebaasidele — kuni 100MB/s. Ja nĂŒĂŒd hoiame viimase kolme kuu logisid koguses umbes 10-15TB, lootes, et kolme kuu jooksul suudab iga arendaja mistahes probleemiga tegeleda.

Me mÔistame probleeme

Aga lihtsalt nende andmete kogumine — see on hea, kasulik, asjakohane, kuid liiga vĂ€he — neid tuleb mĂ”ista. Sest see on miljoneid erinevaid plaane pĂ€evas.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Aga miljonid — see on juhitav, enne tuleb teha seda 'vĂ€hem'. Ja eelkĂ”ige tuleb otsustada, kuidas seda 'vĂ€hem' organiseerida.

Oleme vÀlja toonud endale kolm peamist punkti:

  • kes selle pĂ€ringu saadab
    See, from which application it "arrived": web interface, backend, payment system, or something else.
  • kus this happened
    On which specific server. Because if you have several servers behind one application, and suddenly one "lags" (because the "disk failed", "memory leaked", or some other issue), you need to address it specifically to the server.
  • kuidas the problem manifested in one way or another

To understand "who" sent us the request, we use a standard tool — setting a session variable: SET application_name = '{bl-host}:{bl-method}'; — we set the name of the business logic host from which the request comes, and the name of the method or application that initiated it.

After we have passed the "host" of the request, it needs to be logged — for this, we configure the variable log_line_prefix = ' %m [%p:%v] [%d] %r %a'. For those interested, it may look in the manual, what it all means. It turns out, we see in the log:

  • aega
  • process and transaction identifiers
  • database name
  • IP of the one who sent this request
  • and the name of the method

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Pealegi mĂ”istsime, et pole liiga huvitav vaadata ĂŒhe pĂ€ringu korrelatsiooni erinevate serverite vahel. Harva juhtub, et teil on ĂŒks rakendus, mis samamoodi "laguneb" siin ja seal. Kuid isegi kui see on sama — vaadake mĂ”nda neist serveritest.

Nii et lĂ”ikepinda "ĂŒks server — ĂŒks pĂ€ev" olid meile igasuguseks analĂŒĂŒsiks piisavad.

Esimene analĂŒĂŒtiline lĂ”ige — see on see "mall" — lĂŒhendatud vorm plaani esitamiseks, puhastatud kĂ”igist arvulistest nĂ€itajatest. Teine lĂ”ikepind — rakendus vĂ”i meetod, ja kolmas — see konkreetne plaanipunkt, mis tĂ”i meile probleeme.

Kui me ĂŒhel mÀÀratletud korralt mallidele ĂŒle lĂ€ksime, saime kohe kaks eelist:

  • mĂ€rgatav arv objekti analĂŒĂŒsi vĂ€hendamine
    Selle asemel, et probleemiga tegeleda tuhandete pĂ€ringute vĂ”i plaanide kaudu, vaatame nĂŒĂŒd vaid kĂŒmneid malle.
  • aegjoon
    KokkuvĂ”ttes, kui teatud lĂ”igust "fakte" kokku vĂ”tta, saab nende ilmumist pĂ€eva jooksul visualiseerida. Siinkohal vĂ”ite aru saada, et kui teil on mĂ”ni muster, mis toimub nĂ€iteks kord tunnis, aga peaks olema kord pĂ€evas, tasub mĂ”elda, mis valesti lĂ€ks — kes ja miks selle esitas, vĂ”ib-olla ei peaks seda siin olema. See on veel ĂŒks mittearvuline, puhtalt visuaalne analĂŒĂŒsimeetod.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Teised meetodid pÔhinevad nÀitajatel, mida me plaanist vÀlja töötame: kui mitu korda on selline muster aset leidnud, koguaeg ja keskmine aeg, kui palju andmeid on ketast lugenud ja kui palju mÀlust...

NĂ€iteks, kui te tulete hosti analĂŒĂŒsilehe juurde, nĂ€ete — mingil pĂ”hjusel on ketas hakanud liiga palju lugema. Serveri ketas ei suuda hakkama saada — kes seda lugemist teostab?

VĂ”ite sorteerida igasuguste veergude jĂ€rgi ja otsustada, millega te hetkel tegelema hakkate — protsessori vĂ”i ketase koormuse, vĂ”i ĂŒldise pĂ€ringute arvu... Sortisite, vaatasite "tipud" ĂŒle, parandasite need — tĂ”ukate vĂ€lja uue versiooni rakendusest.
[ĐČĐžĐŽĐ”ĐŸĐ»Đ”Đșцоя]

Ja kohe saate nĂ€ha erinevaid rakendusi, mis töötavad sama ĆĄablooni alusel vastavalt pĂ€ringu tĂŒĂŒbile SELECT * FROM users WHERE login = 'Vasya'. Eesmine, tagumine, töötlemine
 Ja te mĂ”tlete, miks töötlemine peaks lugema kasutajat, kui ta ei suhtle sellega.

Tagasi liikudes - rakenduse kaudu nĂ€ha kohe, mida see teeb. NĂ€iteks, eesmine - see, see, see ja veel see korra tunnis (just nagu ajakava aitab). Ja kohe tekib kĂŒsimus - tundub, et eesmine ei peaks tegema midagi korra tunnis


PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

MĂ”ne aja pĂ€rast saime aru, et meil on puudu koondatud statistika plaani sĂ”lmede lĂ”ikes. Me eraldasime plaanidest ainult need sĂ”lmed, mis tegelevad andmete endi tabelitega (kas loevad/kirjutavad neid indeksi kaudu vĂ”i mitte). Igatahes, eelneva pildi suhtes lisandub vaid ĂŒks aspekt - kui palju kirjeid see sĂ”lm meile tĂ”i, ja kui palju see kĂ”rvale heitis (Rows Removed by Filter).

Teie tabelis ei ole sobivat indeksit, teete sellele pĂ€ringu, see lendab mööda indeksit, langeb Seq Scan
 kĂ”ik kirjed, vĂ€lja arvatud ĂŒks, olete filtreerinud. Aga miks teil on ööpĂ€evas 100M filtreeritud kirjeid, kas ei oleks parem indeksi kasutusele vĂ”tta?

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

KĂ”iki sĂ”lmpunkte puudutavaid plaane analĂŒĂŒsides mĂ”istsime, et teatud tĂŒĂŒpilised struktuurid plaanides nĂ€evad vĂ€ga tĂ”enĂ€oliselt kahtlased vĂ€lja. Arendajale oleks hea öelda: „SĂ”ber, siin loed sa alguses indeksi jĂ€rgi, siis sorteeri ja lĂ”puks lĂ”ika” — tavaliselt on seal ĂŒks rida.

KĂ”ik, kes on selliste patroonidega pĂ€ringuid teinud, on kindlasti kokku puutunud: „Anna mulle Viime viimane tellimus, selle kuupĂ€ev.” Ja kui sul ei ole kuupĂ€eva indeksit vĂ”i kasutatud indeksis ei ole kuupĂ€eva, astud sa just sellistele „harjastele.”

Aga me ju teame, et need on „harjad” — miks mitte kohe arendajale öelda, mida tal peaks tegema. Seega, kui ta nĂŒĂŒd plaani avab, nĂ€eb meie arendaja kohe ilusat pilti vihjetega, kus talle öeldakse: „Sul on siin ja siin probleemid ning need lahenevad nii ja naa.”

Tulemusena on probleemi lahendamiseks vajalik kogemus varasemaga vĂ”rreldes kahanenud mitmeid kordi. Selline tööriist meil nĂŒĂŒd on.

PostgreSQL pÀringute massiline optimeerimine. Kirill Borovikov (Tensor)

Allikas: habr.com

Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster