PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Baie wat reeds gebruik verduidelik.tensor.ru - ons PostgreSQL-planvisualiseringsdiens is dalk nie bewus van een van sy supermoondhede nie - om 'n moeilik-leesbare stuk van die bedienerlogboek om te draai ...

PostgreSQL Query Profiler: Hoe om plan en navraag te pas
… in 'n pragtig ontwerpte navraag met kontekstuele wenke vir die relevante plannodusse:

PostgreSQL Query Profiler: Hoe om plan en navraag te pas
In hierdie transkripsie van die tweede deel van sy verslag by PGConf.Russia 2020 Ek sal jou vertel hoe ons dit reggekry het.

Die transkripsie van die eerste deel, gewy aan tipiese navraagprestasieprobleme en hul oplossings, kan in die artikel gevind word "Resepte vir siek SQL-navrae".



Kom ons doen eers die inkleur - en ons sal nie meer die plan inkleur nie, ons het dit reeds geverf, dit is reeds pragtig en verstaanbaar, maar die versoek.

Dit het vir ons gelyk of die versoek wat met 'n ongeformatteerde "blad" uit die log getrek is, baie lelik lyk en dus ongerieflik.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Veral wanneer ontwikkelaars die versoekliggaam in die kode “gom” (dit is natuurlik 'n anti-patroon, maar dit gebeur) in een reël. Gruwel!

Kom ons teken dit op een of ander manier mooier.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

En as ons dit pragtig kan teken, dit wil sê, die versoekliggaam uitmekaar haal en terugsit, dan kan ons 'n wenk aan elke voorwerp van hierdie versoek "heg" - wat gebeur het op die ooreenstemmende punt in die plan.

Soek sintaksisboom

Om dit te doen, moet die navraag eers ontleed word.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Want, ons het die kern van die stelsel loop op NodeJS, toe het ons 'n module daarvoor gemaak, jy kan vind dit op github. Trouens, dit is uitgebreide "bindings" na die internals van die PostgreSQL-ontleder self. Dit wil sê, die grammatika is eenvoudig binêr saamgestel en bindings word daaraan gemaak deur NodeJS. Ons het ander mense se modules as basis geneem – hier is geen groot geheim nie.

Ons voer die liggaam van die invoerversoek na ons funksie - by die uitset kry ons 'n ontleed sintaksisboom in die vorm van 'n JSON-objek.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Nou kan ons deur hierdie boom in die teenoorgestelde rigting hardloop en die versoek saamstel met die inkepings, kleur, formatering wat ons wil hê. Nee, dit is nie konfigureerbaar nie, maar ons het gedink dit sou gerieflik wees.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Kartering navraag en plan nodusse

Kom ons kyk nou hoe ons die plan wat ons in die eerste stap ontleed het en die navraag wat ons in die tweede ontleed het, kan kombineer.

Kom ons neem 'n eenvoudige voorbeeld - ons het 'n versoek wat 'n CTE vorm en dit twee keer lees. Hy genereer so 'n plan.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

CTE

As jy noukeurig daarna kyk, dit voor die 12de weergawe (of begin daaruit met die sleutelwoord MATERIALIZED) vorming CTE is 'n onvoorwaardelike hindernis vir die skeduleerder.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

En dit beteken, as ons iewers in die versoek die generering van CTE sien en iewers in die plan die nodus CTE, dan "veg" hierdie nodusse uniek onder mekaar, ons kan hulle dadelik kombineer.

Taak "met 'n asterisk"Let wel: CTE's kan geneste word.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas
Daar is baie swak geneste, en selfs dieselfde naam. Byvoorbeeld, jy kan binne CTE A maak CTE X, en op dieselfde vlak binne CTE B weer doen CTE X:

WITH A AS (
  WITH X AS (...)
  SELECT ...
)
, B AS (
  WITH X AS (...)
  SELECT ...
)
...

Wanneer jy vergelyk, moet jy dit verstaan. Dit is baie moeilik om dit met “oë” te verstaan ​​– selfs om die plan te sien, selfs die liggaam van die versoek te sien. As jou CTE-generasie kompleks, geneste is, die versoeke groot is, dan is dit heeltemal onbewustelik.

UNION

As ons 'n sleutelwoord in die versoek het UNION [ALL] (die operateur van die koppeling van twee monsters), dan stem dit in die plan ooreen met óf die nodus Append, of sommige Recursive Union.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Dit wat "bo" is UNION - dit is die eerste kind van ons nodus, wat "van onder" is - die tweede. As deur UNION ons het dan verskeie blokke op een slag "gegom". Append-node sal steeds net een hê, maar dit sal nie twee kinders hê nie, maar baie - in volgorde soos hulle gaan, onderskeidelik:

  (...) -- #1
UNION ALL
  (...) -- #2
UNION ALL
  (...) -- #3

Append
  -> ... #1
  -> ... #2
  -> ... #3

Taak "met 'n asterisk": binne rekursiewe haal generasie (WITH RECURSIVE) kan ook meer as een wees UNION. Maar net die heel laaste blok na die laaste een is altyd rekursief UNION. Alles hierbo is een maar anders UNION:

WITH RECURSIVE T AS(
  (...) -- #1
UNION ALL
  (...) -- #2, тут кончается генерация стартового состояния рекурсии
UNION ALL
  (...) -- #3, только этот блок рекурсивный и может содержать обращение к T
)
...

Sulke voorbeelde moet ook kan "plak". In hierdie voorbeeld sien ons dit UNION-segmente in ons versoek was 3 stukke. Gevolglik een UNION соответствует Append-node, en die ander - Recursive Union.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Lees-skryf data

Alles, uiteengesit, nou weet ons watter stuk van die versoek ooreenstem met watter stuk van die plan. En in hierdie stukke kan ons maklik en natuurlik daardie voorwerpe vind wat "leesbaar" is.

Uit die oogpunt van die navraag weet ons nie of dit 'n tabel of 'n CTE is nie, maar hulle word deur dieselfde nodus aangedui RangeVar. En in die "leesbare" plan is dit ook 'n redelik beperkte stel nodusse:

  • 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]

Ons ken die struktuur van die plan en die versoek, ons ken die korrespondensie van die blokke, ons ken die name van die voorwerpe - ons maak 'n ondubbelsinnige vergelyking.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Weereens taak "met 'n asterisk". Ons neem 'n versoek, voer dit uit, ons het geen aliasse nie - ons lees dit net twee keer van een CTE.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Kom ons kyk na die plan - wat is die moeilikheid? Hoekom het ons 'n alias gehad? Ons het dit nie bestel nie. Waar kry hy so 'n "nommer" vandaan?

PostgreSQL voeg dit self by. Jy moet dit net verstaan net so 'n alias vir ons, vir die doeleindes van vergelyking met die plan, maak dit geen sin nie, dit word eenvoudig hier bygevoeg. Kom ons steur ons nie aan hom nie.

Die tweede taak "met 'n asterisk": as ons van 'n gepartisioneerde tabel lees, sal ons 'n nodus kry Append of Merge Append, wat sal bestaan ​​uit 'n groot aantal "kinders", en elkeen sal 'n paar wees Scan'om van die tabel-afdeling: Seq Scan, Bitmap Heap Scan of Index Scan. Maar in elk geval, hierdie "kinders" sal nie komplekse navrae wees nie - dit is hoe hierdie nodusse onderskei kan word van Append by UNION.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Ons verstaan ​​ook sulke knope, ons versamel hulle "in een hopie" en sê: "alles wat jy van megatable lees, is hier en onder in die boom".

"Eenvoudige" dataverkrygingsnodes

PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Values Scan in plan ooreenstem VALUES in die versoek.

Result is 'n versoek sonder FROM soos SELECT 1. Of wanneer jy 'n valse uitdrukking in het WHERE-blok (dan verskyn die kenmerk One-Time Filter):

EXPLAIN ANALYZE
SELECT * FROM pg_class WHERE FALSE; -- или 0 = 1

Result  (cost=0.00..0.00 rows=0 width=230) (actual time=0.000..0.000 rows=0 loops=1)
  One-Time Filter: false

Function Scan "Mapyatsya" op die SRF met dieselfde naam.

Maar met geneste navrae is alles meer ingewikkeld - ongelukkig verander dit nie altyd in nie InitPlan/SubPlan. Soms verander hulle in ... Join of ... Anti Join, veral wanneer jy iets skryf soos WHERE NOT EXISTS .... En dit is nie altyd moontlik om daar te kombineer nie - in die teks van die plan is daar geen operateurs wat ooreenstem met die nodusse van die plan nie.

Weereens taak "met 'n asterisk": sommige VALUES in die versoek. In hierdie geval en in die plan sal jy verskeie nodusse kry Values Scan.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

"Getal" agtervoegsels sal help om hulle van mekaar te onderskei - dit word presies bygevoeg in die volgorde waarin die ooreenstemmende VALUES-blokke in die loop van die versoek van bo na onder.

Data verwerking

Dit blyk dat alles in ons versoek uitgesorteer is - slegs Limit.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Maar alles is eenvoudig hier - soos nodusse soos Limit, Sort, Aggregate, WindowAgg, Unique Hulle "kaart" een-tot-een aan die ooreenstemmende operateurs in die versoek, as hulle daar is. Daar is geen "sterre" en geen probleme nie.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

SLUIT

Moeilikhede ontstaan ​​wanneer ons wil kombineer JOIN tussen hulleself. Dit is nie altyd moontlik nie, maar dit is moontlik.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Vanuit die navraagontleder se oogpunt het ons 'n nodus JoinExpr, wat presies twee kinders het - links en regs. Dit is onderskeidelik wat "bo" jou JOIN is en wat "onder" dit in die versoek geskryf is.

En uit die oogpunt van die plan is dit twee afstammelinge van sommige * Loop/* Join-nodus. Nested Loop, Hash Anti Join... is so iets.

Kom ons gebruik eenvoudige logika: as ons tabelle A en B het wat mekaar in die plan "aansluit", dan in die navraag kan hulle óf geleë wees A-JOIN-BOf B-JOIN-A. Kom ons probeer om so te kombineer, probeer om op die teenoorgestelde manier te kombineer, en so aan totdat sulke pare opraak.

Vat ons sintaksboom, vat ons plan, kyk na hulle... dit lyk nie so nie!
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Kom ons teken dit oor in die vorm van grafieke – o, iets het al soortgelyk aan iets geword!
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Kom ons let op dat ons nodusse het wat kinders B en C op dieselfde tyd het – dit maak nie vir ons saak in watter volgorde nie. Kom ons kombineer hulle en draai die prentjie van die nodus om.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Kom ons kyk weer. Nou het ons nodusse met kinders A en pare (B + C) - versoenbaar daarmee.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Puik! Dit blyk dat ons hierdie twee is JOIN van die navraag met die nodusse van die plan is suksesvol gekombineer.

Helaas, hierdie probleem word nie altyd opgelos nie.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Byvoorbeeld, as die versoek A JOIN B JOIN C, en in die plan is die "uiterste" nodusse A en C eerstens verbind. Maar daar is nie so 'n operateur in die navraag nie, ons het niks om uit te lig nie, daar is niks om die wenk aan te bind nie. Dieselfde met "komma" wanneer jy skryf A, B.

Maar in die meeste gevalle kry byna alle nodusse dit reg om betyds te "losmaak" en sulke profilering aan die linkerkant te kry - letterlik, soos in Google Chrome, wanneer jy JavaScript-kode ontleed. Jy kan sien hoe lank elke reël en elke stelling "uitgevoer" is.
PostgreSQL Query Profiler: Hoe om plan en navraag te pas

En om dit vir jou makliker te maak om dit alles te gebruik, het ons berging gemaak argief, waar jy jou planne kan stoor en dan saam met gepaardgaande navrae kan vind of 'n skakel met iemand kan deel.

As jy net 'n onleesbare versoek in 'n voldoende vorm moet bring, gebruik ons "normaliseerder".

PostgreSQL Query Profiler: Hoe om plan en navraag te pas

Bron: will.com

Voeg 'n opmerking