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.

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

Minu nimi on Kirill Borovikov, ma esindan . 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 : 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.

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.

Ă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. [] - 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. [] - "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.

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.

Kuna mÔista, mis juhtus tÀpselt sel hetkel, kui pÀring serveris tÀideti, on tarkade inimeste poolt kirjutatud . 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.

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.

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: 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."

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.

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.

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.

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.

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.
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!»

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.

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.

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.

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.

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.'
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.

Tegelikult on olemas ametlik moodul, mis oskab statistikat koguda, kuid seda tuleb samuti konfiguratsioonis aktiveerida â see on . 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.

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.

"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.

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.

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..

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.

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 , 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

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.

Ă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.
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...

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?

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.

Allikas: habr.com
