PostgreSQL Query Profiler: nola lotu plana eta kontsulta
Dagoeneko erabiltzen ari diren asko azaldu.tentsorea.ru - Gure PostgreSQL planen bistaratze-zerbitzuak baliteke bere super-ahalmen baten berri ez izatea - zerbitzariaren erregistroko zati irakurterraza bihurtzea...
... ederki diseinatutako kontsulta batean dagozkien plan-nodoetarako testuinguru-aholkuekin:
Lehenengo zatiaren transkripzioa, ohiko kontsulta-errendimendu-arazoei eta horien konponbideei eskainitakoa, artikuluan aurki daiteke "SQL kontsulta gaiztoen errezetak".
Lehenik eta behin, has gaitezen koloreztatzen - eta ez dugu gehiago margotuko planoa, dagoeneko margotu dugu, dagoeneko ederra eta ulergarria dugu, baina eskaera bat.
Iruditu zitzaigun formatu gabeko "orria" horrekin erregistrotik ateratako eskaera oso itsusia dela eta, beraz, deserosoa dela.
Batez ere, garatzaileek eskaeraren gorputza kodean "kolatzen" dutenean (hau, noski, ereduaren aurkakoa da, baina gertatzen da) lerro batean. Izugarria!
Marraz dezagun nola edo hala ederkiago.
Eta hori ederki marrazten badugu, hau da, eskaeraren gorputza desmuntatu eta berriro elkartu, orduan eskaera honen objektu bakoitzari iradokizun bat "erantsi" diezaiokegu - planoko dagokion puntuan gertatu zena.
Kontsulta sintaxiaren zuhaitza
Horretarako, eskaera aztertu behar da lehenik.
Badugulako sistemaren muina NodeJS-en exekutatzen da, orduan modulu bat egin dugu horretarako, dezakezu aurkitu GitHub-en. Izan ere, hauek PostgreSQL analizatzailearen barneko "loturak" hedatuak dira. Hau da, gramatika konpilatu bitarra besterik ez da eta loturak NodeJS-tik egiten dira. Besteen moduluak hartu genituen oinarritzat - hemen ez dago sekretu handirik.
Eskaeraren gorputza gure funtzioaren sarrera gisa elikatzen dugu - irteeran analizatutako sintaxi-zuhaitza lortzen dugu JSON objektu moduan.
Orain zuhaitz hau zeharkatu dezakegu kontrako norabidean eta eskaera bat muntatu dezakegu nahi ditugun koska, kolore eta formatuekin. Ez, hau ez da pertsonalizagarria, baina komenigarria izango zela iruditu zitzaigun.
Kontsulta eta plan-nodoak mapatzea
Ikus dezagun orain nola uztartu ditzakegun lehenengo urratsean aztertu genuen plana eta bigarrenean aztertu genuen kontsulta.
Har dezagun adibide sinple bat: CTE bat sortzen duen eta bi aldiz irakurtzen duen kontsulta bat dugu. Horrelako plan bat sortzen du.
Horrek esan nahi du CTE belaunaldi bat eskaeran nonbait eta nodo bat planean nonbait ikusten badugu CTE, orduan nodo hauek behin betiko elkarren artean "borrokatzen" dira, berehala konbina ditzakegu.
Arazoa izartxo batekin: CTEak habiaratu daitezke.
Badira oso gaizki habiatuta daudenak, eta baita izen berekoak ere. Adibidez, barruan dezakezu CTE A egiteko CTE X, eta maila berean barruan CTE B egin ezazu berriro CTE X:
WITH A AS (
WITH X AS (...)
SELECT ...
)
, B AS (
WITH X AS (...)
SELECT ...
)
...
Konparatzerakoan, hau ulertu behar duzu. “Begiekin” hori ulertzea –plana ikusi ere, eskaeraren gorputza ikusi ere– oso zaila da. Zure CTE belaunaldia konplexua bada, habiaratua eta eskaerak handiak badira, guztiz inkontzientea da.
UNION
Kontsultan gako-hitz bat badugu UNION [ALL] (bi lagin batzeko eragilea), orduan planoan nodo bati dagokio Append, edo batzuk Recursive Union.
Goian "goian" dagoena UNION - hau gure nodoaren lehen ondorengoa da, hau da, "behean" dagoena - bigarrena. bidezkoa bada UNION hainbat bloke aldi berean “itsatsita” ditugu, bada Append-oraindik nodo bakarra egongo da, baina ez ditu bi, seme-alaba asko baizik - doazen ordenan, hurrenez hurren:
(...) -- #1
UNION ALL
(...) -- #2
UNION ALL
(...) -- #3
Append
-> ... #1
-> ... #2
-> ... #3
Arazoa izartxo batekin: laginketa errekurtsiboaren sorreraren barruan (WITH RECURSIVE) bat baino gehiago ere izan daiteke UNION. Baina azkenaren ondorengo azken blokea bakarrik da beti errekurtsiboa UNION. Goiko guztia bat da, baina ezberdina UNION:
WITH RECURSIVE T AS(
(...) -- #1
UNION ALL
(...) -- #2, тут кончается генерация стартового состояния рекурсии
UNION ALL
(...) -- #3, только этот блок рекурсивный и может содержать обращение к T
)
...
Horrelako adibideak ere "atera" gai izan behar dituzu. Adibide honetan hori ikusten dugu UNION-Gure eskaeran 3 segmentu zeuden. Horren arabera, bat UNION соответствует Append-nodoa, eta besteari - Recursive Union.
Irakurri-idatzi datuak
Dena jarrita dago, orain badakigu eskaeraren zein piezari dagokion planaren zein zatiri. Eta pieza hauetan erraz eta naturaltasunez aurki ditzakegu “irakurgarriak” diren objektu horiek.
Kontsulten ikuspuntutik, ez dakigu taula bat edo CTE bat den, baina nodo berberak izendatzen ditu. RangeVar. Eta "irakurgarritasunari dagokionez", hau ere nodo multzo nahiko mugatua da:
Seq Scan on [tbl]
Bitmap Heap Scan on [tbl]
Index [Only] Scan [Backward] using [idx] on [tbl]
CTE Scan on [cte]
Insert/Update/Delete on [tbl]
Planoaren eta kontsultaren egitura ezagutzen dugu, blokeen korrespondentzia ezagutzen dugu, objektuen izenak ezagutzen ditugu -bat-bateko konparazioa egiten dugu-.
Berriz ere zeregina "izartxo batekin". Eskaera hartzen dugu, exekutatzen dugu, ez dugu aliasik - bi aldiz irakurri dugu CTE beretik.
Planari begiratzen diogu, zein da arazoa? Zergatik genuen alias bat? Ez genuen agindu. Non lortzen du halako “zenbaki-zenbakia”?
PostgreSQL-k berak gehitzen du. Hori ulertu besterik ez duzu behar halako ezizena besterik ez guretzat, planarekin alderatzeko, ez du zentzurik, besterik gabe, hemen gehitzen da. Ez diezaiogun kasurik egin.
Bigarren zeregina "izartxo batekin": zatitutako taula batetik irakurtzen ari bagara, orduan nodo bat lortuko dugu Append edo Merge Append, “ume” ugariz osatuta egongo dena, eta horietako bakoitza nolabait izango da Scan'om taula-ataletik: Seq Scan, Bitmap Heap Scan edo Index Scan. Baina, nolanahi ere, "seme-alabak" hauek ez dira kontsulta konplexuak izango - horrela bereiz daitezke nodo hauek Append at UNION.
Halako korapiloak ere ulertzen ditugu, bildu "pila batean" eta esaten dugu: "megatabletik irakurtzen duzun guztia hemen eta zuhaitzean behera dago".
Datuak jasotzeko nodo "sinpleak".
Values Scan planoan dagokio VALUES eskaeran.
Result gabe eskaera bat da FROM atsegin SELECT 1. Edo nahita espresio faltsu bat daukazunean WHERE-blokea (orduan atributua agertzen da One-Time Filter):
EXPLAIN ANALYZE
SELECT * FROM pg_class WHERE FALSE; -- или 0 = 1
Baina habiaratu diren kontsultekin dena konplikatuagoa da - zoritxarrez, ez dira beti bihurtzen InitPlan/SubPlan. Batzuetan bihurtzen dira ... Join edo ... Anti Join, batez ere antzeko zerbait idazten duzunean WHERE NOT EXISTS .... Eta hemen ez da beti posible horiek konbinatzea - planaren testuan ez dago planaren nodoei dagozkien operadorerik.
Berriz ere zeregina "izartxo batekin": batzuk VALUES eskaeran. Kasu honetan eta planoan hainbat nodo lortuko dituzu Values Scan.
"Zenbakitutako" atzizkiek elkarrengandik bereizten lagunduko dute - dagozkionak aurkitzen diren ordenan gehitzen dira. VALUES-Blokeak eskaeran zehar goitik behera.
Datuen tratamendua
Badirudi gure eskaeran dena konponduta dagoela; geratzen dena da Limit.
Baina hemen dena erraza da, hala nola, nodoak Limit, Sort, Aggregate, WindowAgg, Unique "mapa" bat-batean eskaeran dagozkion operadoreei, baldin badaude. Hemen ez dago "izar" edo zailtasunik.
ELKARTU
Konbinatu nahi dugunean sortzen dira zailtasunak JOIN euren artean. Hau ez da beti posible, baina posible da.
Kontsulta analizatzailearen ikuspuntutik, nodo bat dugu JoinExpr, zehazki bi seme-alaba dituena - ezkerrekoa eta eskuinekoa. Hau da, beraz, zure JOIN "gainean" dagoena eta eskaeran "behean" idatzita dagoena.
Eta planaren ikuspuntutik, hauek batzuen ondorengo bi dira * Loop/* Join-nodoa. Nested Loop, Hash Anti Join,... - horrelako zerbait.
Erabili dezagun logika sinplea: planoan elkar "batzen" diren A eta B taulak baditugu, eskaeran bai koka litezke. A-JOIN-BEdo B-JOIN-A. Saia gaitezen horrela uztartzen, saia gaitezen alderantziz uztartzen, eta horrela halako bikoterik gabe geratu arte.
Har ditzagun gure sintaxi-zuhaitza, hartu gure plana, begira ditzagun... ez antzekoa!
Erremarraz dezagun grafiko moduan - oh, dagoeneko zerbait dirudi!
Kontuan izan dezagun aldi berean B eta C seme-alabak dituzten nodoak ditugula - berdin zaigu zein ordenatan. Konbina ditzagun eta buelta eman ditzagun nodoaren argazkiari.
Begira dezagun berriro. Orain A seme-alabak eta bikoteak (B + C) dituzten nodoak ditugu, haiekin ere bateragarriak.
Bikaina! Bi hauek garela ematen du JOIN eskaeratik plan-nodoekin konbinatu ziren.
Ala ere, arazo hau ez da beti konpontzen.
Adibidez, eskaera batean bada A JOIN B JOIN C, eta planoan, lehenik eta behin, "kanpoko" nodoak A eta C konektatuta zeuden. Baina eskaeran ez dago horrelako operadorerik, ez dugu ezer nabarmentzeko, ez iradokizunik eransteko. Berdin gertatzen da “koma” idazten duzunean A, B.
Baina, kasu gehienetan, ia nodo guztiak "askatu" daitezke eta profil mota hau ezkerrean lor dezakezu denborarekin - literalki, Google Chrome-n bezala JavaScript kodea aztertzen duzunean. Lerro bakoitzak eta adierazpen bakoitzak "exekutatu" zenbat denbora behar izan duen ikus dezakezu.
Eta hori guztia erabiltzea erosoagoa izan dadin, biltegiratzea egin dugu artxiboa, non gorde eta gero zure planak aurki ditzakezu lotutako eskaerekin batera edo esteka norbaitekin parteka dezakezu.
Kontsulta irakurgaitz bat forma egoki batera ekarri behar baduzu, erabili gure "normalizatzailea".