Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

It rapport presintearret guon oanpakken dy't tastean kontrolearje de prestaasjes fan SQL-fragen as d'r miljoenen per dei binne, en d'r binne hûnderten kontrolearre PostgreSQL-tsjinners.

Hokker technyske oplossingen kinne ús sa'n folume fan ynformaasje effisjint ferwurkje, en hoe makket dit it libben fan in gewoane ûntwikkelder makliker?


Wa is ynteressearre? analyze fan spesifike problemen en ferskate optimisaasjetechniken SQL-fragen en it oplossen fan typyske DBA-problemen yn PostgreSQL - jo kinne ek lês in rige artikels oer dit ûnderwerp.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)
Myn namme is Kirill Borovikov, ik fertsjintwurdigje Tensor bedriuw. Spesifyk spesjalisearje ik my yn it wurkjen mei databases yn ús bedriuw.

Tsjintwurdich sil ik jo fertelle hoe't wy queries optimalisearje, as jo de prestaasjes fan ien query net hoege "útinoar te kiezen", mar it probleem massaal oplosse. As d'r miljoenen oanfragen binne, en jo moatte wat fine oanpakken foar oplossing dit grutte probleem.

Yn 't algemien is Tensor foar in miljoen fan ús kliïnten VLSI is ús applikaasje: sosjaal bedriuwsnetwurk, oplossingen foar fideokommunikaasje, foar ynterne en eksterne dokumintstream, boekhâldingsystemen foar boekhâlding en pakhuzen, ... Dat is sa'n "mega-kombinaasje" foar yntegreare bedriuwsbehear, wêryn mear as 100 ferskillende binne ynterne projekten.

Om te soargjen dat se allegear normaal wurkje en ûntwikkelje, hawwe wy 10 ûntwikkelingssintra yn it heule lân, mei mear yn har 1000 ûntwikkelders.

Wy hawwe wurke mei PostgreSQL sûnt 2008 en hawwe in grut bedrach sammele fan wat wy ferwurkje - kliïntgegevens, statistyske, analytyske, gegevens fan eksterne ynformaasjesystemen - mear as 400TB. D'r binne allinich sawat 250 tsjinners yn produksje, en yn totaal binne d'r sawat 1000 databaseservers dy't wy kontrolearje.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

SQL is in deklarative taal. Jo beskriuwe net "hoe" wat moat wurkje, mar "wat" jo wolle berikke. De DBMS wit better hoe't jo in JOIN meitsje kinne - hoe jo jo tabellen kinne ferbine, hokker betingsten jo moatte oplizze, wat sil troch de yndeks gean, wat sil net ...

Guon DBMS's akseptearje hints: "Nee, ferbine dizze twa tabellen yn sa'n en sa'n wachtrige," mar PostgreSQL kin dit net dwaan. Dit is de bewuste posysje fan liedende ûntwikkelders: "Wy wolle leaver de query-optimizer ôfmeitsje dan ûntwikkelders tastean wat soart hints te brûken."

Mar, nettsjinsteande it feit dat PostgreSQL de "bûten" net tastean om himsels te kontrolearjen, lit it perfekt ta sjoch wat der yn him bartas jo jo query útfiere, en wêr't it problemen hat.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Yn it algemien, hokker klassike problemen komt in ûntwikkelder [nei in DBA] meastentiids mei? "Hjir hawwe wy foldien it fersyk, en alles is stadich mei ús, alles hinget, der bart wat... In soarte fan problemen!”

De redenen binne hast altyd itselde:

  • inefficiënt query algoritme
    Untwikkelder: "No jou ik him 10 tabellen yn SQL fia JOIN ..." - en ferwachtet dat syn betingsten wûnderlik effektyf "ûntbûn" wurde en hy sil alles fluch krije. Mar wûnders bart net, en elk systeem mei sa'n fariabiliteit (10 tabellen yn ien FROM) jout altyd in soarte fan flater. [artikel]
  • irrelevante statistiken
    Dit punt is spesifyk heul relevant foar PostgreSQL, as jo in grutte dataset op 'e tsjinner "getten", meitsje in fersyk, en it "sexcanits" jo tablet. Want juster wiene d'r 10 records yn, en hjoed binne d'r 10 miljoen, mar PostgreSQL is dit noch net bewust, en wy moatte it der oer fertelle. [artikel]
  • "plug" op boarnen
    Jo hawwe ynstallearre in grutte en swier laden databank op in swak tsjinner dat hat net genôch skiif, ûnthâld, of prosessor prestaasjes. En dat is alles... Ergens is in prestaasjeplafond dêr't je net mear boppe springe kinne.
  • blokkearjen
    Dit is in lestich punt, mar se binne it meast relevant foar ferskate wizigingsfragen (INSERT, UPDATE, DELETE) - dit is in apart grut ûnderwerp.

In plan krije

...En foar al it oare wy nedich in plan! Wy moatte sjen wat der binnen de server bart.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

In query-útfierplan foar PostgreSQL is in beam fan it query-útfieralgoritme yn tekstfertsjintwurdiging. It is krekt it algoritme dat, as gefolch fan analyse troch de planner, it meast effektyf waard fûn.

Elke beamknooppunt is in operaasje: it opheljen fan gegevens út in tabel of yndeks, it bouwen fan in bitmap, it ferbinen fan twa tabellen, it gearfoegjen, it krúsjen of útsluten fan seleksjes. It útfieren fan in query omfettet kuierjen troch de knopen fan dizze beam.

Om it queryplan te krijen, is de maklikste manier om de ferklearring út te fieren EXPLAIN. Om mei alle echte attributen te kommen, dat is, om eins in query út te fieren op 'e basis - EXPLAIN (ANALYZE, BUFFERS) SELECT ....

It minne diel: as jo it útfiere, bart it "hjir en no", dus it is allinich geskikt foar lokale debuggen. As jo ​​nimme in hege laden tsjinner dat is ûnder in sterke stream fan gegevens feroarings, en do sjochst: "Oh! Hjir hawwe wy in trage útfieringся fersyk." In heal oere, in oere lyn - wylst jo rinnen en dit fersyk krije fan 'e logs, it werombringe nei de tsjinner, binne jo hiele dataset en statistiken feroare. Jo rinne it om te debuggen - en it rint fluch! En jo kinne net begripe wêrom, wêrom wie stadich.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Om te begripen wat der krekt barde op it momint dat it fersyk waard útfierd op 'e tsjinner, skreau tûke minsken auto_explain module. It is oanwêzich yn hast alle meast foarkommende PostgreSQL-distribúsjes, en kin gewoan wurde aktivearre yn it konfiguraasjetriem.

As it beseft dat guon fersyk langer rint dan de limyt dy't jo it ferteld hawwe, docht it it "snapshot" fan it plan fan dit fersyk en skriuwt se tegearre yn it log.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Alles liket no goed te wêzen, wy geane nei it log en sjogge dêr... [tekstfuotdoek]. Mar wy kinne der neat oer sizze, oars as it feit dat it in poerbêst plan is, om't it 11ms duorre om út te fieren.

Alles liket goed te wêzen - mar neat is dúdlik wat der eins bard is. Ofsjoen fan de algemiene tiid, wy sjogge net echt neat. Want nei sa'n "lam" fan platte tekst sjen is oer it algemien net fisueel.

Mar sels as it net dúdlik is, sels as it ûngemaklik is, binne d'r mear fûnemintele problemen:

  • It knooppunt jout oan som fan middels fan 'e hiele subtree ûnder him. Dat is, jo kinne net gewoan útfine hoefolle tiid is bestege oan dizze bepaalde Index Scan as d'r wat nestele betingst ûnder is. Wy moatte dynamysk sjen om te sjen oft d'r "bern" en betingste fariabelen binne, CTE's binnen - en dit alles "yn ús gedachten" ôflûke.
  • Twadde punt: de tiid dy't oanjûn is op it knooppunt is ien node útfiering tiid. As dit knooppunt waard útfierd as gefolch fan bygelyks in loop troch tabelrecords ferskate kearen, dan nimt it oantal loops-syklusen fan dizze knooppunt ta yn it plan. Mar de atomyske útfieringstiid sels bliuwt itselde yn termen fan plan. Dat is, om te begripen hoe lang dizze knoop yn totaal waard útfierd, moatte jo it iene mei it oare fermannichfâldigje - nochris "yn jo holle."

Begryp yn sokke situaasjes "Wa is de swakste skeakel?" hast ûnmooglik. Dêrom skriuwe sels de ûntwikkelders sels yn 'e "hantlieding" dat "In plan begripe is in keunst dy't leard wurde moat, belibje ...".

Mar wy hawwe 1000 ûntwikkelders, en do kinst net oerbringe dizze ûnderfining oan elk fan harren. Ik, jo, hy wit it, mar immen dêr wit it net mear. Miskien sil hy leare, of miskien net, mar hy moat no wurkje - en wêr soe hy dizze ûnderfining krije?

Plan fisualisaasje

Dêrom realisearre wy dat om te gean mei dizze problemen, wy moatte goede fisualisaasje fan it plan. [lidwurd]

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Wy gongen earst "troch de merk" - lit ús op it ynternet sjen om te sjen wat der sels bestiet.

Mar it die bliken dat d'r heul pear relatyf "live" oplossingen binne dy't min of mear ûntwikkelje - letterlik mar ien: explain.depesz.com troch Hubert Lubaczewski. As jo ​​​​it fjild "feed" ynfiere in tekstfertsjintwurdiging fan it plan, toant it jo in tabel mei de parsearde gegevens:

  • knooppunt syn eigen ferwurkingstiid
  • totale tiid foar de hiele subtree
  • oantal records dat waarden ophelle dat waarden statistysk ferwachte
  • it node lichem sels

Dizze tsjinst hat ek de mooglikheid om in argyf fan keppelings te dielen. Jo smieten jo plan deryn en sei: "Hey, Vasya, hjir is in keppeling, d'r is wat mis."

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Mar der binne ek lytse problemen.

As earste, in enoarme hoemannichte "copy-paste". Jo nimme in stik fan 'e log, stek it der yn, en nochris, en nochris.

Twad, gjin analyze fan it bedrach fan gegevens lêzen - deselde buffers dy't útfier EXPLAIN (ANALYZE, BUFFERS), wy sjogge it hjir net. Hy wit gewoan net hoe te disassemble se, begripe se en wurkje mei harren. As jo ​​​​in protte gegevens lêze en beseffe dat jo miskien de skiif en ûnthâldcache ferkeard allocearje, is dizze ynformaasje heul wichtich.

It tredde negative punt is de tige swakke ûntwikkeling fan dit projekt. De commits binne hiel lyts, it is goed as ien kear alle seis moannen, en de koade is yn Perl.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Mar dit is allegear "lyrics", wy koenen op ien of oare manier libje mei dit, mar d'r is ien ding dat ús sterk draaide fan dizze tsjinst. Dit binne flaters yn 'e analyze fan Common Table Expression (CTE) en ferskate dynamyske knopen lykas InitPlan / SubPlan.

As jo ​​dizze ôfbylding leauwe, dan is de totale útfieringstiid fan elke yndividuele knooppunt grutter dan de totale útfieringstiid fan it heule fersyk. It is ienfâldich - de generaasjetiid fan dizze CTE waard net lutsen fan 'e CTE Scan-knooppunt. Dêrom witte wy net mear it goede antwurd op hoe lang de CTE-scan sels duorre.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Doe beseften wy dat it tiid wie om ús eigen te skriuwen - hoera! Elke ûntwikkelder seit: "No sille wy ús eigen skriuwe, it sil super maklik wêze!"

Wy namen in steapel typysk foar web tsjinsten: in kearn basearre op Node.js + Express, brûkt Bootstrap en D3.js foar moaie diagrammen. En ús ferwachtingen wiene folslein terjochte - wy krigen it earste prototype yn 2 wiken:

  • oanpaste plan parser
    Dat is, no kinne wy ​​elk plan analysearje fan dy generearre troch PostgreSQL.
  • korrekte analyze fan dynamyske knopen - CTE Scan, InitPlan, SubPlan
  • analyze fan buffers distribúsje - wêr't gegevenssiden wurde lêzen út it ûnthâld, wêr fan 'e lokale cache, wêr fan skiif
  • krige dúdlikens
    Om dit alles net te "graven" yn it log, mar om de "swakste keppeling" direkt op 'e foto te sjen.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Wy krigen soksawat, mei syntaksismarkearring ynbegrepen. Mar meastal wurkje ús ûntwikkelders net mear mei in folsleine foarstelling fan it plan, mar mei in koartere. Ommers, wy hawwe al parsed alle nûmers en smiten se lofts en rjochts, en yn 'e midden wy lieten allinnich de earste rigel, wat soarte fan knooppunt it is: CTE Scan, CTE generaasje of Seq Scan neffens guon teken.

Dit is de ôfkoarte foarstelling dy't wy neame plan sjabloan.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Wat soe oars handich wêze? It soe handich wêze om te sjen hokker diel fan ús totale tiid wurdt tawiisd oan hokker knooppunt - en gewoan "plakke" oan 'e kant sirkeldiagram.

Wy wize op it knooppunt en sjogge - it docht bliken dat Seq Scan minder dan in kwart fan 'e totale tiid naam, en de oerbleaune 3/4 waard nommen troch CTE Scan. Ôfgriis! Dit is in lytse notysje oer de "fjoerrate" fan CTE Scan as jo se aktyf brûke yn jo fragen. Se binne net heul fluch - se binne inferior, sels foar reguliere tafelscannen. [lidwurd] [lidwurd]

Mar meastal sokke diagrammen binne nijsgjirriger, komplekser, as wy fuortendaliks wize op in segmint en sjogge, bygelyks, dat mear as de helte fan 'e tiid guon Seq Scan "iten". Boppedat siet d'r in soarte fan Filter binnen, in protte records waarden neffens it ferwidere ... Jo kinne dizze foto direkt nei de ûntwikkelder smite en sizze: "Vasya, alles is hjir min foar jo! Stel it út, sjoch - der is wat mis!"

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Fansels wiene d'r wat "raken" belutsen.

It earste wat wy tsjinkamen wie it ôfrondingsprobleem. De tiid fan elke yndividuele knooppunt yn it plan wurdt oanjûn mei in krektens fan 1 μs. En as it oantal knooppuntsyklusen grutter is as bygelyks 1000 - nei útfiering PostgreSQL ferdield "binnen de krektens", dan krije wy by it weromrekkenjen de totale tiid "ergens tusken 0.95ms en 1.05ms". As de telling nei mikrosekonden giet, is dat goed, mar as it al [milli] sekonden is, moatte jo dizze ynformaasje rekken hâlde as jo boarnen "ûntkoppele" oan 'e knopen fan' e "wa konsumearre hoefolle" plan.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

It twadde punt, komplekser, is de ferdieling fan boarnen (dy buffers) ûnder dynamyske knopen. Dit koste ús de earste 2 wiken fan it prototype plus noch 4 wiken.

It is frij maklik om dit soarte fan probleem te krijen - wy dogge in CTE en lêze sabeare wat deryn. Yn feite is PostgreSQL "tûk" en sil dêr neat direkt lêze. Dan nimme wy it earste rekôr derfan, en dêrby de hûndert en earste fan deselde CTE.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Wy sjogge nei it plan en begripe - it is frjemd, wy hawwe 3 buffers (gegevenspagina's) "ferbrûkt" yn Seq Scan, 1 mear yn CTE Scan, en 2 mear yn 'e twadde CTE Scan. Dat is, as wy alles gewoan gearfetsje, sille wy 6 krije, mar fan 'e tablet lêze wy allinich 3! CTE Scan lêst neat fan wêr dan ek, mar wurket direkt mei it proses ûnthâld. Dat is, hjir is dúdlik wat mis!

Yn feite docht bliken dat hjir al dy 3 siden mei gegevens binne dy't opfrege binne fan Seq Scan, earst 1 frege foar de 1e CTE Scan, en dan de 2e, en noch 2 oan him foarlêzen. Dat is, in totaal fan 3 siden waarden lêzen gegevens, net 6.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

En dit byld late ús ta it begryp dat de útfiering fan in plan net langer in beam is, mar gewoan in soarte fan acyclyske grafyk. En wy krigen in diagram lykas dit, sadat wy begripe "wat kaam út wêr yn it earste plak." Dat is, hjir hawwe wy makke in CTE út pg_class, en frege foar it twa kear, en hast al ús tiid waard bestege oan de tûke doe't wy fregen foar it de 2e kear. It is dúdlik dat it lêzen fan de 101e yngong folle djoerder is as gewoan it lêzen fan de 1e yngong fan 'e tablet.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Wy hawwe efkes útblaasd. Se seine: "No, Neo, do witst kung fu! No is ús ûnderfining direkt op jo skerm. No kinne jo it brûke." [lidwurd]

Log konsolidaasje

Us 1000 ûntwikkelders sykhelle in sucht fan opluchting. Mar wy begrepen dat wy mar hûnderten "combat" servers hawwe, en al dizze "copy-paste" fan 'e kant fan' e ûntwikkelders is hielendal net handich. Wy realisearre dat wy it sels sammelje moasten.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Yn 't algemien is d'r in standertmodule dy't statistiken sammelje kin, mar it moat ek aktivearre wurde yn' e konfiguraasje - dit module pg_stat_statements. Mar hy paste ús net.

As earste wurdt it tawize oan deselde fragen mei ferskate skema's binnen deselde database ferskillende QueryIds. Dat is, as jo earst dogge SET search_path = '01'; SELECT * FROM user LIMIT 1;en dan SET search_path = '02'; en itselde fersyk, dan sil de statistiken fan dizze module hawwe ferskillende records, en ik sil net by steat wêze om te sammeljen algemiene statistiken spesifyk yn it ramt fan dit fersyk profyl, sûnder rekken hâldend mei de regelingen.

It twadde punt dat ús foarkommen hat it te brûken is gebrek oan plannen. Dat is, der is gjin plan, der is allinnich it fersyk sels. Wy sjogge wat wat fertrage, mar wy begripe net wêrom. En hjir komme wy werom nei it probleem fan in rap feroarjende dataset.

En it lêste momint - gebrek oan "feiten". Dat is, jo kinne gjin spesifike eksimplaar fan query-útfiering oanpakke - d'r is gjinien, d'r binne allinich aggregearre statistiken. Hoewol't it mooglik is om te wurkjen mei dizze, it is gewoan hiel dreech.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Dêrom hawwe wy besletten om copy-paste te fjochtsjen en begon te skriuwen collector.

De samler ferbynt fia SSH, stelt in feilige ferbining mei de tsjinner mei de databank mei help fan in sertifikaat, en tail -F "klemt" oan it yn it logbestân. Dus yn dizze sesje wy krije in folsleine "spegel" fan de hiele log triem, dy't de tsjinner genereart. De lading op de tsjinner sels is minimaal, om't wy dêr neat parse, wy spegelje gewoan it ferkear.

Om't wy de ynterface yn Node.js al begûnen te skriuwen, bleaune wy ​​de samler yn te skriuwen. En dizze technology hat himsels rjochtfeardige, om't it heul handich is om JavaScript te brûken om te wurkjen mei swak opmakke tekstgegevens, dat is it log. En de Node.js-ynfrastruktuer sels as in backend-platfoarm lit jo maklik en maklik wurkje mei netwurkferbiningen, en yndie mei alle gegevensstreamen.

Dêrtroch "stretch" wy twa ferbiningen: de earste om "harkje" nei it log sels en nim it nei ússels, en de twadde om periodyk freegje de basis. "Mar it log lit sjen dat it teken mei oid 123 is blokkearre," mar dit betsjut neat foar de ûntwikkelder, en it soe moai wêze om de databank te freegjen, "Wat is OID = 123 dochs?" En sa freegje wy de basis periodyk wat wy noch net fan ússels witte.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

"D'r is mar ien ding dat jo net rekken holden hawwe, d'r is in soarte fan oaljefantlike bijen! .." Wy begûnen dit systeem te ûntwikkeljen doe't wy 10 tsjinners kontrolearje woene. De meast krityske yn ús begryp, wêr't guon problemen ûntstienen dy't lestich om te gean. Mar yn it earste kwart krigen wy hûndert foar tafersjoch - om't it systeem wurke, elkenien woe, elkenien wie noflik.

Dit alles moat tafoege wurde, de gegevensstream is grut en aktyf. Yn feite, wat wy kontrolearje, wat wy kinne omgean, is wat wy brûke. Wy brûke ek PostgreSQL as gegevensopslach. En neat is rapper om gegevens yn te "goaien" dan de operator COPY Noch net.

Mar gewoan "útjaan" gegevens is net echt ús technology. Want as jo sawat 50k oanfragen per sekonde hawwe op hûndert servers, dan sil dit 100-150GB oan logs per dei generearje. Dêrom moasten wy de basis foarsichtich "snije".

Earst hawwe wy it dien partitioning by day, om't, troch en grut, gjinien is ynteressearre yn de korrelaasje tusken dagen. Wat makket it út wat jo juster hiene, as jo fannacht in nije ferzje fan 'e applikaasje útrôle - en al wat nije statistiken.

Twads, wy learden (wurden twongen) heul, heul rap om te skriuwen mei help COPY. Dat is, net allinnich COPYwant hy is flugger as INSERT, en noch flugger.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

It tredde punt - ik moast abandon triggers, respektivelik, en bûtenlânske kaaien. Dat is, wy hawwe hielendal gjin referinsjele yntegriteit. Want as jo in tabel hawwe dy't in pear FK's hat, en jo sizze yn 'e databankstruktuer dat "hjir in logrecord is dat troch FK bygelyks ferwiisd wurdt nei in groep records," dan as jo it ynfoegje, PostgreSQL hat neat mear, mar hoe te nimmen it en doch it earlik SELECT 1 FROM master_fk1_table WHERE ... mei de identifier dy't jo besykje yn te foegjen - gewoan om te kontrolearjen dat dit record dêr oanwêzich is, dat jo dizze frjemde kaai net "ôfbrekke" mei jo ynfoegje.

Ynstee fan ien rekord nei de doeltabel en syn yndeksen, krije wy it ekstra foardiel fan lêzen fan alle tabellen wêr't it nei ferwiist. Mar wy hawwe dit hielendal net nedich - ús taak is om safolle mooglik en sa fluch mooglik op te nimmen mei de minste lading. Dus FK - del!

It folgjende punt is aggregaasje en hashing. Yn it earstoan hawwe wy se yn 'e databank ymplementearre - it is ommers handich om it fuortendaliks, as in rekord komt, te dwaan yn in soarte fan tablet "plus ien" rjocht yn 'e trekker. No, it is handich, mar itselde minne ding - jo ynfoegje ien record, mar wurde twongen om te lêzen en skriuwe wat oars út in oare tabel. Boppedat lês en skriuwst net allinnich, mar doch it ek elke kear.

Stel jo no foar dat jo in tabel hawwe wêryn jo gewoan it oantal oanfragen telle dat troch in spesifike host is trochjûn: +1, +1, +1, ..., +1. En jo hawwe dit yn prinsipe net nedich - it is allegear mooglik som yn it ûnthâld op 'e samler en stjoer yn ien kear nei de databank +10.

Ja, yn gefal fan guon problemen kin jo logyske yntegriteit "útinoar falle", mar dit is in hast ûnrealistysk gefal - om't jo in normale server hawwe, it hat in batterij yn 'e controller, jo hawwe in transaksjelog, in log op' e bestânsysteem ... Yn 't algemien is it it net wurdich. It ferlies fan produktiviteit dy't jo krije fan it útfieren fan triggers / FK is de kosten dy't jo meitsje net wurdich.

It is itselde mei hashing. In bepaald fersyk fljocht nei jo ta, jo berekkenje dêr in bepaalde identifier út yn 'e databank, skriuwe it nei de databank en fertelle it dan oan elkenien. Alles is goed oant, op it momint fan opnimmen, in twadde persoan nei jo komt dy't itselde ding wol opnimme - en jo wurde blokkearre, en dit is al min. Dêrom, as jo de generaasje fan guon ID's kinne oerdrage oan de kliïnt (relatyf oan de databank), is it better om dit te dwaan.

It wie gewoan perfekt foar ús om MD5 út 'e tekst te brûken - fersyk, plan, sjabloan, ... Wy berekkenje it op 'e samlerkant, en "goat" de klear makke ID yn 'e databank. De lingte fan MD5 en deistige partitioning tastean ús net te soargen oer mooglike botsingen.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Mar om dit alles fluch op te nimmen, moasten wy de opnameproseduere sels oanpasse.

Hoe skriuwe jo normaal gegevens? Wy hawwe in soarte fan dataset, wy splitst it yn ferskate tabellen, en dan COPY it - earst yn 'e earste, dan yn' e twadde, yn 'e tredde ... It is ûngemaklik, om't wy lykje ien gegevensstream yn trije stappen te skriuwen opfolgjend. Onaangenaam. Kin it flugger dien wurde? Kinne!

Om dit te dwaan is it genôch om dizze streamen parallel mei elkoar te ûntbinen. It docht bliken dat wy flaters, oanfragen, sjabloanen, blokkearjen, ... fleane yn aparte triedden - en wy skriuwe it allegear parallel. Genôch foar dit hâld in COPY-kanaal konstant iepen foar elke yndividuele doeltafel.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Dat wol sizze by de samler der is altyd in stream, wêryn ik de gegevens dy't ik nedich kin skriuwe. Mar sadat de databank dizze gegevens sjocht, en immen komt net fêst te wachtsjen op dizze gegevens om te skriuwen, COPY moat wurde ûnderbrutsen op bepaalde yntervallen. Foar ús wie de meast effektive perioade sawat 100ms - wy slute it en iepenje it fuortendaliks wer nei deselde tafel. En as wy net genôch hawwe fan ien stream yn guon pieken, dan dogge wy pooling oant in bepaalde limyt.

Derneist hawwe wy fûn dat foar sa'n loadprofyl elke aggregaasje, as records yn batches sammele wurde, kwea is. Klassyk kwea is INSERT ... VALUES en fierder 1000 records. Om't jo op dat stuit in skriuwpeak hawwe op 'e media, en elkenien dy't besykje wat op' e skiif te skriuwen sil wachtsje.

Om fan sokke anomalies kwyt te reitsjen, aggregearje gewoan neat, net buffer hielendal. En as buffering nei skiif foarkomt (gelokkich lit de Stream API yn Node.js jo útfine) - stel dizze ferbining út. As jo ​​​​in evenemint ûntfange dat it wer fergees is, skriuw it dan út 'e opboude wachtrige. En wylst it drok is, nim dan de folgjende frije út it swimbad en skriuw nei it.

Foardat wy dizze oanpak foar gegevensopname yntrodusearje, hienen wy sawat 4K skriuwopsjes, en op dizze manier fermindere wy de lading mei 4 kear. No binne se noch 6 kear groeid troch nije kontroleare databases - oant 100MB / s. En no bewarje wy logs foar de lêste 3 moannen yn in folume fan sawat 10-15TB, yn 'e hoop dat yn mar trije moannen elke ûntwikkelder elk probleem kin oplosse.

Wy begripe de problemen

Mar gewoan al dizze gegevens sammelje is goed, nuttich, relevant, mar net genôch - it moat begrepen wurde. Want dit binne miljoenen ferskillende plannen per dei.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Mar miljoenen binne net te behearjen, wy moatte earst "lytser" dwaan. En earst moatte jo beslute hoe't jo dit "lytsere" ding sille organisearje.

Wy hawwe trije wichtige punten identifisearre:

  • wa stjoerde dit fersyk
    Dat is, fan hokker applikaasje hat it "oankommen": webynterface, backend, betellingssysteem of wat oars.
  • wêr it barde
    Op hokker spesifike tsjinner? Want as jo ferskate tsjinners hawwe ûnder ien applikaasje, en ynienen wurdt ien "dom" (om't de "skiif rot is", "ûnthâld lekt", in oar probleem), dan moatte jo de tsjinner spesifyk oanpakke.
  • hoe it probleem manifestearre him op ien of oare wize

Om te begripen "wa" stjoerde ús in fersyk, wy brûke in standert ark - it ynstellen fan in sesje fariabele: SET application_name = '{bl-host}:{bl-method}'; - wy stjoere de namme fan 'e bedriuwslogika-host wêrfan it fersyk komt, en de namme fan' e metoade of applikaasje dy't it inisjearre.

Nei't wy de "eigner" fan it fersyk hawwe trochjûn, moat it wurde útfierd nei it log - hjirfoar konfigurearje wy de fariabele log_line_prefix = ' %m [%p:%v] [%d] %r %a'. Foar belangstellenden, miskien sjoch yn 'e hânliedingwat betsjut it allegear. It docht bliken dat wy sjogge yn it log:

  • время
  • proses en transaksje identifiers
  • databank namme
  • IP fan de persoan dy't stjoerde dit fersyk
  • en metoade namme

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Doe realisearre wy dat it net heul ynteressant is om te sjen nei de korrelaasje foar ien fersyk tusken ferskate servers. It is net faak dat jo in situaasje hawwe wêrby't ien applikaasje hjir en dêr likefolle skroeven. Mar sels as it is itselde, sjoch op ien fan dizze tsjinners.

Dus hjir is de besuniging "ien tsjinner - ien dei" it blykte ús genôch te wêzen foar elke analyze.

De earste analytyske seksje is itselde "foarbyld" - in ôfkoarte foarm fan presintaasje fan it plan, wiske fan alle numerike yndikatoaren. De twadde besuniging is de applikaasje of metoade, en de tredde besuniging is de spesifike planknooppunt dy't ús problemen feroarsake.

Doe't wy fan spesifike gefallen nei sjabloanen ferhuze, krigen wy twa foardielen tagelyk:

  • meardere reduksje yn it oantal objekten foar analyse
    Wy moatte it probleem net mear analysearje troch tûzenen fragen of plannen, mar troch tsientallen sjabloanen.
  • tiidline
    Dat is, troch gearfetting fan 'e "feiten" binnen in bepaalde seksje, kinne jo har uterlik oerdeis werjaan. En hjir kinne jo begripe dat as jo in soarte fan patroan hawwe dat bart, bygelyks ien kear yn 'e oere, mar it moat ien kear deis barre, jo moatte tinke oer wat der mis gie - wa hat it feroarsake en wêrom, miskien moat it hjir wêze moat net. Dit is in oare net-numerike, suver fisuele, metoade fan analyze.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

De oerbleaune metoaden binne basearre op de yndikatoaren dy't wy út it plan ekstrahearje: hoefolle kearen sa'n patroan barde, de totale en gemiddelde tiid, hoefolle gegevens waarden lêzen fan 'e skiif, en hoefolle út it ûnthâld ...

Om't jo bygelyks op 'e analytyske side foar de host komme, sjoch - wat begjint te folle te lêzen op' e skiif. De skiif op de tsjinner kin it net oan - wa lêst derfan?

En jo kinne sortearje op elke kolom en beslute wat jo no sille dwaan - de lading op 'e prosessor of de skiif, of it totale oantal oanfragen ... in nije ferzje fan 'e applikaasje útrôle.
[fideolêzing]

En fuortendaliks kinne jo sjen ferskate applikaasjes dy't komme mei deselde sjabloan út in fersyk lykas SELECT * FROM users WHERE login = 'Vasya'. Frontend, backend, ferwurkjen ... En jo freegje jo ôf wêrom't ferwurking de brûker soe lêze as hy net mei him omgiet.

De tsjinoerstelde manier is om direkt fan 'e applikaasje te sjen wat it docht. Bygelyks, de frontend is dit, dit, dit, en dit ien kear yn 'e oere (de tiidline helpt). En de fraach komt fuortdaliks op: it liket derop dat it net de taak is fan de frontend om ien kear yn de oere wat te dwaan...

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Nei in skoft realisearren wy dat wy aggregearre miste statistiken troch plan knopen. Wy isolearre fan 'e plannen allinich de knopen dy't wat dogge mei de gegevens fan' e tabellen sels (lêze / skriuwe se troch yndeks of net). Yn feite wurdt mar ien aspekt tafoege relatyf oan de foarige foto - hoefolle records hat dizze knooppunt ús brocht?, en hoefolle waarden wegereard (Rijen fuortsmiten troch filter).

Jo hawwe gjin geskikte yndeks op 'e plaat, jo meitsje in fersyk nei it, it fljocht foarby de yndeks, falt yn Seq Scan ... jo hawwe filtere út alle records útsein ien. Wêrom hawwe jo 100M filtere records per dei nedich? Is it net better om de yndeks op te rôljen?

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Nei it analysearjen fan alle plannen knooppunt foar knooppunt, realisearre wy dat d'r guon typyske struktueren binne yn 'e plannen dy't heul wierskynlik fertocht sille útsjen. En it soe moai wêze om de ûntwikkelder te fertellen: "Freon, hjir lêze jo earst troch yndeks, dan sortearje, en dan ôfsnien" - as regel is d'r ien rekord.

Elkenien dy't fragen skreaun hat is wierskynlik dit patroan tsjinkaam: "Jou my de lêste bestelling foar Vasya, de datum." En as jo gjin yndeks op datum hawwe, of d'r is gjin datum yn 'e yndeks dy't jo brûkt hawwe, dan sille jo stap op krekt deselde "rake".

Mar wy witte dat dit in "rake" is - dus wêrom net fuortendaliks de ûntwikkelder fertelle wat hy moat dwaan. Sadwaande, by it iepenjen fan in plan no, sjocht ús ûntwikkelder fuortendaliks in prachtige foto mei tips, wêrby't se him daliks sizze: "Jo hawwe hjir en dêr problemen, mar se wurde sa en sa oplost."

Dêrtroch is de hoemannichte ûnderfining dy't nedich wie om problemen oan it begjin en no op te lossen gâns sakke. Dit is it soarte ark dat wy hawwe.

Bulk-optimalisaasje fan PostgreSQL-fragen. Kirill Borovikov (Tensor)

Boarne: www.habr.com

Add a comment