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

Ettekandes esitatakse mÔned lÀhenemised, mis vÔimaldavad jÀlgida SQL-pÀringute tulemuslikkust, kui neid on miljoneid pÀevas, samas kui kontrollitavaid PostgreSQL servereid on sadu.

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

MĂ€ngi videot

Kellele on huvitav konkreetselt probleemide analĂŒĂŒs ja erinevad tehnilised optimeerimise tehnikad SQL-pĂ€ringutes ja tavaliste DBA-ĂŒlesannete lahendused PostgreSQL-is — saab ka tutvuda artiklite seeriaga sellel teemal.

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

TĂ€na rÀÀgin teile, kuidas me tegeleme pĂ€ringute optimeerimisega, kui peate mitte "opereerima" ĂŒhe ainulaadse pĂ€ringu jĂ”udlust, vaid lahendama massiprobleemi. Kui pĂ€ringute arv on miljon, ja peate leidma mingid lahenduste lĂ€henemised selle suure probleemi lahendamiseks.

Tegelikult on "Tensor" miljoni meie kliendi jaoks SBiS — meie rakendus: ettevĂ”tte sotsiaalne vĂ”rgustik, lahendused videokĂ”nede jaoks, sise- ja vĂ€lisdokumendihaldus, raamatupidamise ja lao juhtimissĂŒsteemid,
 Seega on see nagu "megakombain" kompleksseks Ă€rijuhtimiseks, kus on ĂŒle 100 erineva sisemiselt projekti.

Kuna kĂ”ik need peavad korralikult töötama ja arenema — meil on 10 arenduskeskust kogu riigis, milles töötab ĂŒle 1000 arendaja.

Me töötame PostgreSQL-iga alates 2008. aastast ja oleme kogunud suure hulga andmeid — need on kliendiandmed, statistilised, analĂŒĂŒtilised, andmed vĂ€lisest teabe sĂŒsteemidest — ĂŒle 400TB.AinuĂŒksi tootmises on meil umbes 250 serverit, kokku jĂ€lgime andmebaasi servereid umbes 1000.

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

SQL on deklaratiivne keel. Te kirjeldate mitte seda, "kuidas" midagi peab töötama, vaid seda, "mida" soovite saada. Andmebaas teab paremini, kuidas teha JOIN — kuidas teie tabelid ĂŒhendada, milliseid tingimusi seada, mis lĂ€heb indeksi kaudu, mis mitte


MĂ”ned andmebaasisĂŒsteemid vĂ”tavad vastu vihjeid: "Ei, ĂŒhenda need kaks tabelit sellise jĂ€rjekorra jĂ€rgi", kuid PostgreSQL ei oska nii teha. See on teadlik positsioon peamiste arendajate seas: "Paremini tĂ€iustame pĂ€ringu optimeerijat, kui lubame arendajatel kasutada mingeid vihjeid."

Kuid hoolimata sellest, et PostgreSQL ei luba "vÀljast" end hallata, vÔimaldab see suurepÀraselt nÀha, mis toimub "sees", kui te oma pÀringut tÀidate, ja kust vÔivad tekkida probleemid.

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

Üldiselt, milliste klassikaliste probleemidega tuleb arendaja [DBA-le] tavaliselt? "Siin me tĂ€itsime pĂ€ringu ja meil on kĂ”ik aeglane, kĂ”ik on hangunud, midagi toimub... TĂ”eline hĂ€da!"

PÔhjused on peaaegu alati samad:

  • efektiivne pĂ€ringu algoritm
    Arendaja: "Praegu liitun SQL-is 10 tabelit JOIN-iga..." – ja ootab, et tema tingimused imekombel tĂ”husalt "lahendatakse" ja ta saab kĂ”ik kiiresti. Kuid imesid ei juhtu ning iga sĂŒsteem sellise variatiivsuse (10 tabelit ĂŒhes FROM-is) tĂ”ttu annab alati mingi eksituse. [artikkel]
  • aktuaalne statistika
    See hetk on eriti oluline just PostgreSQL-i puhul, kui olete suure andmestiku serverisse "laadinud", teete pĂ€ringu – ja see "lĂ€bivaatab" tabelit. Kuna eile oli seal 10 kirjet, aga tĂ€na 10 miljonit, kuid PostgreSQL ei ole sellest veel teadlik ning peate seda talle nĂ€itama. [artikkel]
  • "ressursside ummistus"
    Te olete pannud suure ja raskesti koormatud andmebaasi nĂ”rgale serverile, millel ei jĂ€tku ketta, mĂ€lu ja protsessori jĂ”udlust. Ja kĂ”ik... Kusagil on jĂ”udluspiir, mille ĂŒletamine ei ole enam vĂ”imalik.
  • lukud
    See on keeruline teema, kuid need on kĂ”ige aktuaalsemad erinevate muudetavate pĂ€ringute (INSERT, UPDATE, DELETE) puhul – see on eraldi suur teema.

Kava saamine


 Ja kĂ”igi ĂŒlejÀÀnud jaoks on meil vajalik kava! Me peame nĂ€gema, mis toimub serveri sees.

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

PostgreSQL-i pĂ€ringu tĂ€itmise plaan on pĂ€ringu tĂ€itmise algoritmi puu tekstiline esitlemine. Just see algoritm, mis analĂŒsaator on tunnustanud kĂ”ige tĂ”husamaks.

Iga puu sĂ”lm on operation: andmete hankimine tabelist vĂ”i indeksist, bitikaardi loomine, kahe tabeli ĂŒhendamine, ĂŒhendamine, lĂ”ikamine vĂ”i valikute eraldamine. PĂ€ringu tĂ€itmine on selle puu sĂ”lme kaudu minek.

KĂŒsimise plaani saamiseks on kĂ”ige lihtsam viis tĂ€ita kĂ€sk EXPLAIN. Et saada kĂ”igi tegelike atribuutidega, st tegelikult pĂ€ringut andmebaasis tĂ€ita – EXPLAIN (ANALYZE, BUFFERS) SELECT ....

Halb sich anu, kui te seda teete, toimub see "siin ja praegu", seega sobib see ainult lokaalseks silumiseks. Kui vĂ”tate nĂ€iteks mĂ”ne suure koormusega serveri, mille all toimub suurt andmevoogu, ja nĂ€ete: "Oih! Siin on meil aeglane tĂ€itminekone. kĂŒsitlus." Pool tundi, tund tagasi – kuni te jooksisite ja tĂ”ite selle pĂ€ringu logidest, kandsite selle serverisse tagasi, on kogu teie andmekogum ja statistika muutunud. Teete seda silumiseks – ja see tĂ€itub kiiresti! Ja te ei saa aru "miks", miks oli aeglane.

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

Kuna mÔista, mis juhtus tÀpselt sel hetkel, kui pÀring serveris tÀideti, on tarkade inimeste poolt kirjutatud moodul auto_explain. See on kohal praktiliselt kÔigis levinud PostgreSQL jaotustes, ning seda saab lihtsalt aktiveerida seadistamisfailis.

Kui see mÔistab, et mÔni pÀring tÀitub kauem kui teatud piir, mille olete talle mÀÀranud, siis teeb ta "snapshot" selle pÀringu plaanist ja kirjutab selle logisse.

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

Nagu kĂ”ik on nĂŒĂŒd hĂ€sti, lĂ€heme logisse ja nĂ€eme seal
 [portjanka teksti]. Kuid me ei saa sellest midagi öelda, peale selle, et see on suurepĂ€rane plaan, kuna see tĂ€itus 11 ms.

Nagu kĂ”ik on hĂ€sti – aga mitte midagi ei ole selge, mis tegelikult juhtus. Ainus, mida me nĂ€eme, on ĂŒleĂŒldine aeg. Sest vaadata sellist "latukĂąt" plain text on ĂŒldse mitte visuaalne.

Aga isegi kui see ei ole visuaalne, on seal ka tÔsisemad probleemid:

  • SĂ”lmes on nĂ€idatud ressursside summa kogu allpuu tema all. Seega on lihtsalt vĂ”imatu teada, kui palju aega konkreetse Index Scan'i jaoks kulus – seda ei saa teha, kui selle all on mĂ”ni sisemine tingimus. Peame dĂŒnaamiliselt vaatama, kas seal sees on "laste" ja tingimuslikke muutujaid, CTE – ja kĂ”ik need ĂŒles arvestama "meeles".
  • Teine hetk: aeg, mis sĂ”lmes on nĂ€idatud, on sĂ”lme ainulaadne tĂ€itmis aeg.Kui see sĂ”lm tĂ€ideti nĂ€iteks tabeli ridade ringkĂ€igu tulemusena mitu korda, siis plaanis suureneb loops – tsĂŒklite arv selles sĂ”lmes. Kuid selle ainulaadne tĂ€itmise aeg jÀÀb plaanis endiseks. Seega, et mĂ”ista, kui kaua see sĂ”lm kokkuvĂ”ttes tĂ€ideti, tuleb ĂŒht korrutada teisega – jĂ€lle "meeles".

Sellest tulenevalt on peaaegu vĂ”imatu mĂ”ista, "Kes on kĂ”ige nĂ”rgem lĂŒli?" SeetĂ”ttu kirjutavad isegi arendajad oma "juhendis", et "Plani mĂ”istmine on kunst, mida tuleb Ă”ppida, kogemus...".

Kuid meil on 1000 arendajat ja seda kogemust ei saa igaĂŒhele pĂ€he panna. Mina, sina, tema - teavad, aga keegi seal kaugel - ei tea. VĂ”ib-olla ta Ă”pib, aga vĂ”ib-olla ei Ă”pi, aga töötama peab ta kohe – kust kĂŒll vĂ”tta seda kogemust.

Plani visualiseerimine

SeetÔttu mÔistsime, et nende probleemide lahendamiseks on meil vaja head plaanivisualiseerimist. [artikkel]

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

LĂ€ksime kĂ”igepealt „turule” - otsime internetist, mis olemas on.

Kuid selgus, et suhteliselt "elavaid" lahendusi, mis enam-vĂ€hem arenevad, on ÀÀrmiselt vĂ€he - vaid ĂŒks: explain.depesz.com Hubert Lubaczewski poolt. Sa annad sisendiks tekstilise plaani, see nĂ€itab sulle tabelit analĂŒĂŒsitud andmetest:

  • enda sĂ”lme töötamise aeg
  • koguaeg kogu alampuu puhul
  • ekstraktitud kirje arv ja statistiliselt oodatud arv
  • sĂ”lme enda keha

Selle teenuse juures on ka vĂ”imalus jagada linkide arhiveeritud andmeid. Sa viskad sinna oma plaani ja ĂŒtled: "Hei, Vassja, siin on sulle link, seal on midagi valesti."

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

Kuid on ka mÔned vÀikesed probleemid.

Esiteks, tohutu hulk "copy-paste'i". Sa vĂ”tad logi tĂŒkikese, lisad selle sisse ja jĂ€lle, ja jĂ€lle.

Teiseks, andmete lugemise arvu analĂŒĂŒsi ei ole - neid buffers, mida toob vĂ€lja EXPLAIN (ANALYZE, BUFFERS), siin me ei nĂ€e. Ta lihtsalt ei oska neid analĂŒĂŒsida, mĂ”ista ja nendega töötada. Kui loed palju andmeid ja mĂ”istad, et vĂ”id valele viisil andmed kettale ja mĂ€llu paigutada, on see teave vĂ€ga oluline.

Kolmas negatiivne moment on selle projekti vÀga nÔrk areng. Commitid on vÀga vÀikesed, hÀsti kui kaks korda aastas, ja kood on Perl'is.

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

Aga need on kĂ”ik "liirika", sellega saaks kuidagi elada, kuid on ĂŒks asi, mis tĂ”ukab meid sellest teenusest kaugele. Need on Common Table Expression (CTE) ja erinevate dĂŒnaamiliste sĂ”lmede nagu InitPlan/SubPlan analĂŒĂŒsivead.

Kui usaldada seda pilti, siis on iga eraldiseisva sÔlme koguaeg pikem kui kogu pÀringu koguaeg. KÔik on lihtne - CTE Scan sÔlmedest ei lahutatud CTE genereerimise aega. SeetÔttu ei tea me enam Ôiget vastust, kui kaua CTE skaneerimine aega vÔttis.

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

Siin mĂ”istsime, et on aeg kirjutada oma — hurraa! Iga arendaja ĂŒtleb: «NĂŒĂŒd kirjutame oma, super lihtne saab olema!»

VĂ”tsime tĂŒĂŒpilise web-teenuste tehnoloogia: pĂ”hijoon on Node.js + Express, lisasime Bootstrap'i ja ilusate diagrammide jaoks D3.js. Ja meie ootused tĂ€itusid tĂ€iesti — esimese prototĂŒĂŒbi saime kahe nĂ€dalaga:

  • oma plaani parseri
    See tĂ€hendab, et nĂŒĂŒd saame analĂŒĂŒsida iga plaani, mida genereerib PostgreSQL.
  • korrektne dĂŒnaamiliste sĂ”lmede analĂŒĂŒs — CTE Scan, InitPlan, SubPlan
  • pufferite jaotuse analĂŒĂŒs — kust loetakse andmete lehed mĂ€lust, kust kohalikust vahemĂ€lust, kust kettalt
  • sai selgus
    Et mitte logides seda kÔike «kaevata», vaid nÀha «nÔrka kohta» kohe pildilt.

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

Saime umbes sellise pildi — kohe sĂŒnteksi esiletoomisega. Kuid tavaliselt töötavad meie arendajad juba mitte kogu plaani, vaid lĂŒhenenud versiooniga. KĂŒll aga oleme kĂ”ik numbrid juba Ă€ra parseerinud ja paremale vasakule kĂ”rvale pannud, ning keskele jĂ€tnud ainult esimese rea, mis sĂ”lm see on: CTE Scan, CTE genereerimine vĂ”i Seq Scan mingi tabeli kohta.

Seda lĂŒhendatud esitlemist nimetame plaani malliks.

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

Mis oleks veel mugav? Oleks mugav nĂ€ha, milline osa millise sĂ”lme kohta kogu ajast jaotub — ja lihtsalt «kleepisime» kĂŒljele torti diagrammina.

Suunates sĂ”lmele nĂ€eme — meie Seq Scan on koguaeg kulutanud vĂ€hem kui veerandi, kuid ĂŒlejÀÀnud 3/4 ajast on kulunud CTE Scan. Kohutav! See vĂ€ike mĂ€rk CTE Scan «kiirusest», kui te aktiivselt neid oma pĂ€ringutes kasutate. Need ei ole eriti kiired — nad kaotavad isegi tavapĂ€rase tabeli skaneerimisele. [artikkel] [artikkel]

Kuid tavaliselt on sellised diagrammid huvitavamad ja keerukamad, kui suuname kohe segmenti ja nĂ€eme nĂ€iteks, et rohkem kui pool kogu ajast on «söötnud» mingi Seq Scan. Ja seal sees oli mingi Filter, hulk kirjeid visati selle kaudu
 Selle pildi saab arendajale otse edasi saata ja öelda: «Vassilij, sul on siin tegelikult kĂ”ik halvasti! Uuri vĂ€lja, vaata — midagi on vale!»

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

Muidugi ei saanud me ilma «aukudeta» hakkama.

Esimene probleem, millega kokku puutusime, oli ĂŒmarus. Iga plaani sĂ”lme aeg on mÀÀratud tĂ€psusega 1 ÎŒs. Ja kui sĂ”lme tsĂŒklite arv ĂŒletab nĂ€iteks 1000 — pĂ€rast PostgreSQL-i töötlemist jagatakse see 'tĂ€psusega', mille tagajĂ€rjel saame koguaeg 'kuskil 0.95 ms ja 1.05 ms vahel'. Kui rÀÀgime mikrosekunditest, pole veel hullu, aga kui juba millisekunditest — siis tuleb plaani sĂ”lmede ressursse 'lahendades' arvestada, kellel kui palju on kokku nĂ”utud.

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

Teine, keerulisem, probleem on ressursside (neid samu pufferid) jaotamine dĂŒnaamilistes sĂ”lmedes. See maksis meile prototĂŒĂŒbi esimestel kahel nĂ€dalal veel lisaks 4 nĂ€dalat.

Sellise probleemi saamine on ĂŒsna lihtne — teeme CTE ja seal nĂ€iliselt loeme midagi. Tegelikult on PostgreSQL 'nutikas' ja ei loe seal otse midagi. Hiljem vĂ”tame sellest esimesse kirje, ja sellele — saja esimese samast CTE-st.

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

Vaadates plaani, mĂ”istame — kummaline, meil oli 3 pufferit (andmelehti), mis 'kasutati' Seq Scanis, veel 1 CTE Scanis ja veel 2 teises CTE Scanis. Kui kĂ”ike kokku liita, siis saame 6, aga tabelist lugesime me vaid 3! CTE Scan ei loe ju midagi kuskilt, vaid töötab otse protsessi mĂ€luga. Seega on siin ilmselgelt midagi valesti!

Tegelikult selgub, et need 3 andmelehte, mis olid nÔutud Seq Scanis, taotles esiteks 1. CTE Scan ja siis 2., ning sellele lugesid nad veel 2. Seega loeti kokku 3 andmelehte, mitte 6.

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

Ja see pilt viis meid arusaamisele, et plaani tĂ€itmine ei ole enam puu, vaid lihtsalt mingi suvaline suunamata graaf. Ja meil on tekkinud umbes selline diagramm, et me mĂ”istaksime 'kust ja kuidas teavet saime'. Siin loodime CTE pg_class-ist ja palusime seda kaks korda ning peaaegu kogu meie aeg kulus haru peal, kui kĂŒsisime seda teist korda. On selge, et 101. kirje lugemine on tunduvalt kallim kui lihtsalt 1. tabelist.

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

Me hingasime mĂ”neks ajaks vĂ€lja. Ütlesime: 'NĂŒĂŒd, Neo, sa tead kung fu! NĂŒĂŒd on meie kogemus sul otse ekraanil. NĂŒĂŒd saad seda kasutada.' [artikkel]

Logide konsolideerimine

Meie 1000 arendajat hingasid kergendatult. Kuid me mĂ”istsime, et meil on ainult sadu „tööservereid“ ja kogu see arendajate poolt tehtud „kopeerimine ja kleepimine“ pole sugugi mugav. Me otsustasime, et peame selle ise kokku panema.

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

Tegelikult on olemas ametlik moodul, mis oskab statistikat koguda, kuid seda tuleb samuti konfiguratsioonis aktiveerida — see on moodul pg_stat_statements. Kuid see meid ei rahuldanud.

Esiteks, sama pĂ€ringutele erinevates skeemides ĂŒhe andmebaasi piires annab see erinevad QueryId. Nii et kui kĂ”igepealt teha SET search_path = '01'; SELECT * FROM user LIMIT 1;, ja siis SET search_path = '02'; ja sama pĂ€ring, siis selle mooduli statistikasse jÀÀvad erinevad kirjed ning ma ei saa koguda ĂŒldist statistikat just selle pĂ€ringuprofiili lĂ”ikes, skeeme arvesse vĂ”tmata.

Teine asi, mis takistas meid selle kasutamist — puuduvad plaanid.Nii et plaani pole, on ainult pĂ€ring ise. Me nĂ€eme, mis aeglustas, kuid ei saa aru, miks. Ja siin tuleme tagasi kiiresti muutuva andmestiku probleemini.

Ja viimane asi — puuduvad „faktid“.Nii et ei saa pöörduda konkreetse pĂ€ringu tĂ€itmise exemplaari poole — seda ei ole, on ainult koondstatistika. Sellega on kuigi vĂ”imalik töötada, lihtsalt vĂ€ga keeruline.

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

SeetĂ”ttu otsustasime „kopeerimise ja kleepimisega“ vĂ”idelda ja hakkasime kirjutama kogujat..

Kogujaga ĂŒhendatakse SSH kaudu, „loob“ sertifikaadi abil turvalise ĂŒhenduse andmebaasi serveriga ja tail -F „kinni” logifailile. Nii et selles seansis saame tĂ€ieliku „peegli“ kĂ”igest logifailist,mida server genereerib. Serveri koormus on sel juhul minimaalne, kuna me seal midagi ei analĂŒĂŒsi, lihtsalt peegeldame liiklust.

Kuna olime juba hakanud kirjutama liidest Node.js-is, jÀtkasime koguja kirjutamist samal platvormil. Ja see tehnoloogia Ôigustas end, kuna halvasti vormindatud tekstiliste andmetega, millega logid on, töötamine JavaScriptiga on vÀga mugav. Ja Node.js infrastruktuur backend-platvormina vÔimaldab hÔlpsalt ja mugavalt töötada vÔrguliideseid ja andmevooge.

Seega me "venitame" kaks ĂŒhendust: esimene, et "kuulata" logi ja see enda juurde tuua, ning teine, et perioodiliselt andmebaasilt kĂŒsida. "Logis on kirjas, et tabel oid 123 on blokeeritud," kuid arendajale ei ĂŒtle see midagi ja oleks hea kĂŒsida andmebaasilt: "Mis asi on OID = 123?" Nii kĂŒsime me perioodiliselt andmebaasilt seda, mida me veel ei tea.

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

"Ainult ĂŒhte asja sa ei arvestanud: on olemas elevantide sarnased mesilased!.." Me alustasime selle sĂŒsteemi arendamist, kui soovisime jĂ€lgida 10 serverit. Need olid meie arvates kĂ”ige kriitilisemad, kus esines probleeme, millega oli raske tegeleda. Kuid juba esimesel kvartalil saime jĂ€lgimiseks sada — sest sĂŒsteem "töötas", kĂ”ik soovisid seda, kĂ”igile oli see mugav.

KĂ”ik need andmed tuleb kokku liita, andmevoog on suur ja aktiivne. Tegelikult jĂ€lgime me seda, millega oskame tegeleda — ja kasutame seda. Andmete hoidmiseks kasutame samuti PostgreSQL-i. Sest pole midagi kiiremat, kui andmeid sinna "valada". COPY pole veel.

Kuid lihtsalt andmete "valamine" ei ole pÀris meie tehnoloogia. Sest kui teil on sajale serverile umbes 50k pÀringut sekundis, siis genereerib see teile 100-150GB logisid pÀevas. SeetÔttu pidime andmebaasi ettevaatlikult "lÔikama".

Esiteks tegime jaotuse pĂ€evade kaupa, sest suurem osa inimesi ei huvita ööpĂ€evade vaheline korrelatsioon. Mis vahet seal on, mis sul eile oli, kui sa öösel vĂ€ljastasid uue rakenduse versiooni — ja juba on mingid uued statistilised andmed.

Teiseks Ôppisime (olid sunnitud) vÀga-vÀga kiiresti kirjutama COPY. See tÀhendab, et mitte lihtsalt COPY, sest see on kiirem kui INSERT, vaid veelgi kiirem.

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

Kolmas punkt — pidime loobuma triggereist ja seega ka vĂ€lismaistest vĂ”tmetest. See tĂ€hendab, et meil pole ĂŒldse viidatud terviklikkust. Sest kui teil on tabel, kus on paar FK-d, ja te ĂŒtlete andmebaasi struktuuris, et "siin on logikirje, mis viitab nĂ€iteks rĂŒhmale kirjed", siis kui te seda sisestate, ei jÀÀ PostgreSQL-il muud ĂŒle kui vĂ”tta ja ausalt teostada SELECT 1 FROM master_fk1_table WHERE ... selle identifikaatoriga, mida te ĂŒritate sisestada — lihtsalt selleks, et kontrollida, et see kirje seal on, et te ei "katke" oma sisestamisega seda vĂ€lismaist vĂ”tit.

Me saame sihtmĂ€rkide tabelisse ja tema indeksitesse ĂŒhe kirje asemel veel lugedes kĂ”igist tabelitest, millele see viitab. Ja me ei vaja seda - meie ĂŒlesanne on salvestada nii palju kui vĂ”imalik ja nii kiiresti kui vĂ”imalik vĂ”imalikult vĂ€ikese koormusega. Seega FK - Ă€ra!

JĂ€rgmine punkt - agregatsioon ja hashimine. Alguses oli see meil realiseeritud andmebaasis - see on mugav, kui mĂ€rge saabub, teha mingis tabelis „pluss ĂŒks“ otse triggeris. HĂ€sti, mugav, kuid halvasti - te lisate ĂŒhe kirje ja peate lugema ja kirjutama veel midagi teisest tabelist. Lisaks sellele, et lugeda ja kirjutada - peate seda tegema iga kord.

Ja nĂŒĂŒd kujutage ette, et teil on tabel, kus te lihtsalt loendate konkreetse hosti kaudu lĂ€bitud pĂ€ringute arvu: +1, +1, +1, ..., +1. Ja teil pole seda tĂ”eliselt vaja - kĂ”ike seda saab mĂ€lus kollektoris kokku lugeda ja saata andmebaasi korraga +10.

Jah, teil vĂ”ib mingi tĂ”rke korral „laguneda“ loogiline terviklikkus, kuid see on praktiliselt ebatĂ”enĂ€oline juhtum - sest teil on korralik server, sellel on kontrolleris aku, teil on tehingute ajakiri, ajakiri failisĂŒsteemis... ÜhesĂ”naga, see ei ole seda vÀÀrt. See ei ole vÀÀrt jĂ”udluse kadu, mille te saate triggerite/FK töötamise tĂ”ttu, nende kulude tĂ”ttu, mida te kannate.

Sama kehtib ka hashimise kohta. Teie poole tuleb mingi pĂ€ring, millest te arvutate andmebaasis mingi identifikaatori, kirjutate selle andmebaasi ja ĂŒtlete kĂ”igile seejĂ€rel. KĂ”ik on hĂ€sti, kuni kirjutamise hetkel tuleb Teile teine soovija selle sama kirjutamiseks - ja teil tekib lukustus, mis juba on halb. SeetĂ”ttu, kui saate mĂ”ne ID genereerimise kliendile (andmebaasi suhtes) ĂŒle anda, on parem seda teha.

Meil on olnud ideaalne kasutada MD5 teksti - pĂ€ring, plaan, mall,
 Arvutame selle kollektoris ja „valame“ andmebaasi juba valmis ID. MD5 pikkus ja pĂ€evane osadus vĂ”imaldavad meil mitte muretseda vĂ”imalike konfliktide pĂ€rast.

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

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

Kuidas andmeid tavaliselt kirjutatakse? Meil on mingi andmestik, me jaotame selle mitmeks tabeliks ja seejĂ€rel COPY — esmalt ĂŒhte, seejĂ€rel teise, kolmandasse... Ebamugav, kuna me nĂ€iliselt kirjutame ĂŒhe andmevoo jĂ€rjestikku kolme sammu jooksul. Mitte just meeldiv. Kas saaks kuidagi kiiremini? Jah!

Selleks piisab lihtsalt, kui jaotada need vood paralleelselt ĂŒksteisega. Tulemuseks on see, et meil on eraldi voogudes vead, pĂ€ringud, mallid, blokeeringud... — ja me kirjutame seda kĂ”ike paralleelselt. Selleks piisab hoida pidevalt avatud COPY-kanalit igale eraldi sihttabelile..

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

See tĂ€hendab, et kollektsionÀÀril on alati voog,kuhu ma saan kirjutada mulle vajalikke andmeid. Kuid et andmebaas neid andmeid nĂ€eks ja keegi ei jÀÀks blokeeringutesse ootama, kuni need andmed kirjutatakse, peab COPY-d katkestama teatud regulaarsusega.Meie jaoks osutus kĂ”ige tĂ”husamaks perioodiks umbes 100 ms — sulgeme ja kohe avame jĂ€lle sama tabeli peale. Ja kui meil ei piisa ĂŒhest voost teatud tipptundidel, siis teeme ummikseisu teatud piirini.

Lisaks selgus, et sellise koormusprofiili jaoks on iga agregatsioon, kui kirjed kogutakse pakettidesse - see on kuritegu. KliĆĄee kuritegu on INSERT ... VALUES ja edasi 1000 kirjet. Sest selle hetkel tekib teil pĂŒsiva kirjutamise tipp ja kĂ”ik teised, kes ĂŒritavad midagi kettale kirjutada, peavad ootama.

Sellest anomaaliast vabanemiseks Ă€rge agregige mitte midagi, Ă€rge buferdage ĂŒldse. Ja kui kettale buferdamine siiski tekib (Ă”nneks, Stream API Node.js-is vĂ”imaldab seda teada), siis lĂŒkake see ĂŒhendus edasi. Kui teil tuleb teade, et see on jĂ€lle vaba — kirjutage sellesse akumuleeritud jĂ€rjekorrast. Ja seni, kuni see on hĂ”ivatud — vĂ”tke jĂ€rgmisest vabast basseinist ja kirjutage sellesse.

Enne sellise lĂ€henemise rakendamist andmete kirjutamisele oli meil umbes 4K kirjutamisoperatsiooni, kuid selle meetodiga vĂ€hendasime koormust nelja korra vĂ”rra. NĂŒĂŒd oleme suuremaks kasvanud veel kuue korra uute jĂ€lgitavate aluste tĂ”ttu — kuni 100MB/s. Ja nĂŒĂŒd hoiame viimase kolme kuu logisid umbes 10–15TB mahus, lootes, et kolme kuu jooksul suudab iga arendaja lahendada iga probleemi.

MÔistame probleeme

Ainult kĂ”igi nende andmete kogumine on hea, kasulik ja vajalik, kuid see ei ole piisav — neid peab mĂ”istma. Sest see on miljonite erinevate plaanide hulk pĂ€evas.

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

Aga miljonid on ĂŒle jĂ”u kĂ€iv, seega tuleb esmalt muuta need „vĂ€ikesemaks”. Ja enne kĂ”ike tuleb otsustada, kuidas te selle „vĂ€ikesuse” korraldate.

Oleme vÀlja toonud kolm peamist punkti:

  • kes selle pĂ€ringu saatis
    St. millisest rakendusest see „tuli”: veebi liides, tagapind, maksesĂŒsteem vĂ”i midagi muud.
  • kus kus see toimus
    Millisel konkreetsel serveril. Sest kui ĂŒhe rakenduse all on mitu serverit, ja Ă€kki ĂŒks „toimetab aeglaselt” (sest „ketas on katki”, „mĂ€lu lekkib”, mingi muu probleem), siis tuleb adresseerida just sellele serverile.
  • kuidas konkreetselt probleem ilmnes selles vĂ”i teises plaanis

Kuna me peame selgitama „kes” saatis meile pĂ€ringu, kasutame standardset tööriista — seansi muutuja seadistamist: SET application_name = '{bl-host}:{bl-method}'; — salvestame Ă€riloogika hosti nime, kust pĂ€ring tuleb, ja meetodi vĂ”i rakenduse nime, mis selle algatas.

PĂ€rast seda, kui oleme edastanud pĂ€ringu „omaniku”, tuleb see logisse vĂ€lja tuua — selleks konfigureerime muutuja log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Kellele see huvi pakub, vĂ”ib vaadata juhendist, mida see kĂ”ik tĂ€hendab. Nii nĂ€eme logis:

  • aega
  • protsessi ja tehingu tuvastajad
  • andmebaasi nime
  • selle isiku IP, kes saatis selle pĂ€ringu
  • ja meetodi nime

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

Edasi liikudes mĂ”istsime, et ĂŒhe pĂ€ringu vahel erinevate serverite sĂŒnergiat pole just vĂ€ga huvitav vaadata. Harva juhtub, et ĂŒhel rakendusel on siin-seal sama „probleem”. Kuid isegi kui see on sama — vaadake mĂ”nda neist serveritest.

Nii et â€žĂŒhe serveri — ĂŒhe pĂ€eva” lĂ”ike oli meile igasuguseks analĂŒĂŒsi jaoks piisav. Esimene analĂŒĂŒtiline lĂ”ige on just see

„mall” — lĂŒhendatud kujul plaani esitus, kust on eemaldatud kĂ”ik numbrilised nĂ€itajad. Teine lĂ”ige — rakendus vĂ”i meetod, ja kolmas — see konkreetne plaan, mis tekitas meile probleeme. Kui me liikuma hakkasime konkreetsetest eksemplaridest mallide juurde, saime kohe kaks eeliseid:

oluliselt vĂ€henenud analĂŒĂŒsitavate objektide arv

  • Problem ei pea enam olema tuhandete pĂ€ringute vĂ”i plaanide pĂ”hjal, vaid kĂŒmnete mallide pĂ”hjal.
    ajajoone
  • ajalugu
    KokkuvĂ”ttes saab „faktide“ ĂŒldistamisel mingisuguse lĂ”ike raames nĂ€idata nende esinemist pĂ€eva jooksul. Siin vĂ”ite mĂ”ista, et kui teil toimub mingisugune muster nĂ€iteks korra tunnis, aga peaks olema korra pĂ€evas, tasub mĂ”elda, mis valesti lĂ€ks — kes ja miks selle kĂ€ivitas, ehk ei peaks seda siin olema. See on veel ĂŒks mitte-numeeriline, visuaalne analĂŒĂŒsimeetod.

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

ÜlejÀÀnud meetodid pĂ”hinevad nĂ€itajatel, mida me plaanist vĂ€lja loome: kui tihti selline muster esines, summaarne ja keskmine aeg, kui palju andmeid on kettalt vĂ€lja loetud ja kui palju mĂ€lust...

Kuna nĂ€iteks tulete hosti analĂŒĂŒsile, vaatate — midagi liiga palju ketast lugema hakkas. Kett serveris ei suuda enam hakkama saada — aga kes seda loeb?

Ja te saate sorteerida igasuguste veergude jĂ€rgi ja otsustada, millega soovite kohe tegeleda — kas protsessori vĂ”i ketta koormusega, vĂ”i ĂŒldise pĂ€ringute arvuga... Sorteerisite, vaatasite „tippe“, parandasite — vĂ€lja lastud uus rakenduse versioon.
[videolektuur]

Ja koheselt nĂ€ete erinevaid rakendusi, mis kasutavad sama mustrit pĂ€ringu tĂŒĂŒbi SELECT * FROM users WHERE login = 'Vasya'. Fronteer, tagumine, töötlemine... Ja te mĂ”tlete, miks töötlemine peaks kasutajat lugema, kui ta temaga ei suhtle.

Tagurpidi meetod — rakendusest kohe nĂ€ha, mida see teeb. NĂ€iteks, fronteer — see, see, see, ja veel see kord tunnis (just ajajoone abil aitab). Ja kohe tekib kĂŒsimus — ilmselt ei peaks fronteer tegema midagi kord tunnis...

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

MĂ”ne aja pĂ€rast mĂ”istsime, et meil puudub koondstatistika plaani sĂ”lmede lĂ”ikes. Me eraldasime plaanidest ainult need sĂ”lmed, mis tegelevad andmetega endi tabelite (loevad/kirjutavad neid indeksi jĂ€rgi vĂ”i mitte). Sisuliselt lisatakse eelnevale pildile vaid ĂŒks aspekt —kui palju kirjeid see sĂ”lm meile tĂ”i , ja kui palju eemaldati (Rows Removed by Filter).Teil pole tabeli jaoks sobivat indeksit, teete sellele pĂ€ringu, see möödub indeksist, satub Seq Scan'i... kĂ”ik kirjed, vĂ€lja arvatud ĂŒks, on filtreeritud. Ja milleks teile 100M filtreeritud kirjet ööpĂ€evas, poleks parem indeks peale panna?

Teil pole sobivat indeksit tabelis, teete sellele pĂ€ringu, see möödub indeksist, langeb Seq Scan
 kĂ”ik kirjed, vĂ€lja arvatud ĂŒks, olete filtreerinud. Aga miks vajate 100 miljonit filtreeritud kirjet ĂŒhe pĂ€eva jooksul, kas poleks parem indeks luua?

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

AnalĂŒĂŒsinud kĂ”iki sĂ”lmpunkte, mĂ”istsime, et plaanides on teatud tĂŒĂŒpilised struktuurid, mis tĂ”enĂ€oliselt nĂ€evad kahtlased vĂ€lja. Ja oleks hea, kui arendajat oleks vĂ”imalik suunata: "SĂ”ber, siin sa kĂ”igepealt loed indeksi kaudu, seejĂ€rel sorteerid ja siis kĂ€rbid" – reeglina on seal ĂŒks kirje.

KÔik, kes on sellise mustriga pÀringute tegemisega kokku puutunud, on tÔenÀoliselt pidanud silmitsi seisma: "Anna mulle viimane tellimus Vassil, kuupÀev". Kui sul pole kuupÀeva indeksi, vÔi kasutatud indeksis pole kuupÀeva, siis astud just nende "kivideni".

Aga me ju teame, et need on "kivid" – siis miks mitte kohe arendajale öelda, mida tal vĂ”iks teha. Seega avades nĂŒĂŒd plaani, nĂ€eb meie arendaja kohe ilusat pilti vihjetega, kus talle öeldakse: "Sul on siin ja siin probleemid, ja need lahenevad nii ja naa."

Tulemuseks on koguse kogemus, mis oli vajalik probleemide lahendamiseks alguses ja praegu, vĂ€henenud kĂŒmneid kordi. Selline tööriist meil ongi tekkinud.

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

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster