PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Ko'pchilik allaqachon foydalanmoqda tushuntirish.tensor.ru - Bizning PostgreSQL rejasini vizualizatsiya qilish xizmatimiz o'zining ajoyib kuchlaridan birini bilmasligi mumkin - server jurnalining o'qish qiyin bo'lgan qismini aylantirish...

PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak
... mos reja tugunlari uchun kontekstli maslahatlar bilan chiroyli tarzda ishlab chiqilgan so'rovga:

PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak
Uning ikkinchi qismining ushbu transkripsiyasida PGConf.Russia 2020 da hisobot Buni qanday uddalaganimizni sizga aytaman.

Oddiy so'rovlarni bajarish muammolari va ularning echimlariga bag'ishlangan birinchi qismning transkripsiyasini maqolada topish mumkin "Zabatli SQL so'rovlari uchun retseptlar".



Birinchidan, rang berishni boshlaylik - va biz endi rejani rang bermaymiz, biz allaqachon rangga bo'yab qo'yganmiz, bizda allaqachon chiroyli va tushunarli, ammo iltimos.

Bizga formatlanmagan "varaq" bilan jurnaldan olib tashlangan so'rov juda xunuk va shuning uchun noqulay ko'rinadi.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Ayniqsa, ishlab chiquvchilar koddagi so'rovning tanasini "yopishtirganda" (bu, albatta, antipattern, lekin shunday bo'ladi) bir qatorda. Dahshatli!

Keling, buni qandaydir chiroyliroq chizamiz.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Va agar biz buni chiroyli tarzda chiza olsak, ya'ni so'rovning tanasini qismlarga ajratib, yig'a olsak, unda biz ushbu so'rovning har bir ob'ektiga ishorani "qo'shishimiz" mumkin - rejaning tegishli nuqtasida nima sodir bo'lgan.

So'rovlar sintaksisi daraxti

Buning uchun avval so'rovni tahlil qilish kerak.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Chunki bizda bor tizimning yadrosi NodeJS da ishlaydi, keyin biz buning uchun modul yaratdik, mumkin GitHub-da toping. Aslida, bular PostgreSQL tahlilchisining ichki qismlariga kengaytirilgan "bog'lash" dir. Ya'ni, grammatika oddiygina ikkilik kompilyatsiya qilingan va unga bog'lanishlar NodeJS dan qilingan. Biz boshqa odamlarning modullarini asos qilib oldik - bu erda katta sir yo'q.

Biz so'rovning tanasini funktsiyamizga kirish sifatida beramiz - chiqishda biz JSON ob'ekti ko'rinishidagi tahlil qilingan sintaksis daraxtini olamiz.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Endi biz ushbu daraxt bo'ylab teskari yo'nalishda yugurishimiz va o'zimiz xohlagan chekinishlar, rang berish va formatlash bilan so'rov yig'ishimiz mumkin. Yo'q, buni sozlash mumkin emas, lekin bizga bu qulay bo'lib tuyuldi.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Xaritalash so'rovi va tugunlarni rejalashtirish

Keling, birinchi bosqichda tahlil qilgan rejani va ikkinchi bosqichda tahlil qilgan so'rovni qanday birlashtirishni ko'rib chiqaylik.

Oddiy misolni olaylik - bizda CTE ni yaratadigan va undan ikki marta o'qiladigan so'rov mavjud. U shunday reja tuzadi.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

CTE

Agar siz diqqat bilan qarasangiz, 12-versiyagacha (yoki undan kalit so'z bilan boshlab MATERIALIZED) shakllanishi CTE rejalashtiruvchi uchun mutlaq to'siqdir.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Bu shuni anglatadiki, agar biz so'rovning bir joyida CTE avlodini va rejaning biror joyida tugunni ko'rsak CTE, keyin bu tugunlar, albatta, bir-biri bilan "kurashadi", biz ularni darhol birlashtira olamiz.

Yulduzcha bilan muammo: CTE'larni joylashtirish mumkin.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak
Juda yomon joylashtirilganlar va hatto bir xil nomdagilar ham bor. Masalan, siz ichkariga kirishingiz mumkin CTE A qilish CTE X, va ichkarida bir xil darajada CTE B yana qiling CTE X:

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

Taqqoslashda siz buni tushunishingiz kerak. Buni "ko'zlaringiz bilan" tushunish - hatto rejani ko'rish, hatto so'rovning mazmunini ko'rish - juda qiyin. Agar sizning CTE avlodingiz murakkab, ichki o'rnatilgan va so'rovlar katta bo'lsa, u butunlay behush.

Union

Agar so'rovda kalit so'z bo'lsa UNION [ALL] (ikkita namunani birlashtirish operatori), keyin rejada u yoki tugunga mos keladi Append, yoki ba'zilari Recursive Union.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Yuqorida "yuqorida" bo'lgan narsa UNION - bu bizning tugunimizning birinchi avlodi, u "pastda" - ikkinchisi. Agar o'tib ketsa UNION bizda bir vaqtning o'zida bir nechta bloklar "yopishgan" Append-hali faqat bitta tugun bo'ladi, lekin u ikkita emas, balki ko'p bolali bo'ladi - ular borish tartibida:

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

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

Yulduzcha bilan muammo: ichida rekursiv namuna olish avlodi (WITH RECURSIVE) bir nechta boΚ»lishi ham mumkin UNION. Lekin faqat oxirgi blokdan keyingi oxirgi blok har doim rekursivdir UNION. Yuqoridagi hamma narsa bitta, ammo boshqacha UNION:

WITH RECURSIVE T AS(
  (...) -- #1
UNION ALL
  (...) -- #2, Ρ‚ΡƒΡ‚ кончаСтся гСнСрация стартового состояния рСкурсии
UNION ALL
  (...) -- #3, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ этот Π±Π»ΠΎΠΊ рСкурсивный ΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΎΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ ΠΊ T
)
...

Bundan tashqari, siz bunday misollarni "yopishtira" olishingiz kerak. Ushbu misolda biz buni ko'ramiz UNION-Bizning so'rovimizda 3 ta segment bor edi. Shunga ko'ra, bitta UNION mos keladi Append-tugun va boshqasiga - Recursive Union.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Ma'lumotlarni o'qish-yozish

Hamma narsa tartibga solingan, endi biz so'rovning qaysi qismi rejaning qaysi qismiga mos kelishini bilamiz. Va bu qismlarda biz osongina va tabiiy ravishda "o'qilishi mumkin bo'lgan" narsalarni topishimiz mumkin.

So'rovlar nuqtai nazaridan, biz bu jadval yoki CTE ekanligini bilmaymiz, lekin ular bir xil tugun tomonidan belgilanadi. RangeVar. Va "o'qilishi" nuqtai nazaridan, bu ham juda cheklangan tugunlar to'plami:

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

Biz reja va so'rovning tuzilishini bilamiz, bloklarning mosligini bilamiz, ob'ektlarning nomlarini bilamiz - biz birma-bir taqqoslaymiz.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Yana "yulduzcha bilan" topshirig'i. Biz so'rovni qabul qilamiz, uni bajaramiz, bizda taxalluslar yo'q - biz uni bir xil CTE dan ikki marta o'qiymiz.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Biz rejaga qaraymiz - muammo nimada? Nega bizda taxallus bor edi? Biz buyurtma bermadik. U bunday "raqam raqamini" qayerdan oladi?

PostgreSQL uni o'zi qo'shadi. Siz shunchaki buni tushunishingiz kerak xuddi shunday taxallus biz uchun, reja bilan taqqoslash uchun, bu hech qanday ma'noga ega emas, bu erda oddiygina qo'shiladi. Unga e'tibor bermaylik.

ikkinchi "yulduzcha bilan" topshirig'i: agar biz bo'lingan jadvaldan o'qiyotgan bo'lsak, biz tugunni olamiz Append yoki Merge Append, ular ko'p sonli "bolalar" dan iborat bo'ladi va ularning har biri qandaydir tarzda bo'ladi Scan'om jadval bo'limidan: Seq Scan, Bitmap Heap Scan yoki Index Scan. Ammo, har qanday holatda, bu "bolalar" murakkab so'rovlar bo'lmaydi - bu tugunlarni shu tarzda ajratish mumkin. Append da UNION.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Biz ham bunday tugunlarni tushunamiz, ularni "bir qoziqda" yig'amiz va aytamiz: "megatable-dan o'qigan hamma narsa shu erda va daraxtda".

"Oddiy" ma'lumotlarni qabul qilish tugunlari

PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Values Scan rejaga mos keladi VALUES so'rovda.

Result bo'lmagan so'rovdir FROM kabi SELECT 1. Yoki ataylab noto'g'ri ifodaga ega bo'lganingizda WHERE-blok (keyin atribut paydo bo'ladi 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 Xuddi shu nomdagi SRFlarga "xarita".

Ammo ichki so'rovlar bilan hamma narsa murakkabroq - afsuski, ular har doim ham aylanavermaydi InitPlan/SubPlan. Ba'zan ular aylanadi ... Join yoki ... Anti Join, ayniqsa siz shunga o'xshash narsalarni yozsangiz WHERE NOT EXISTS .... Va bu erda ularni birlashtirish har doim ham mumkin emas - reja matnida rejaning tugunlariga mos keladigan operatorlar mavjud emas.

Yana "yulduzcha bilan" topshirig'i: biroz VALUES so'rovda. Bunday holda va rejada siz bir nechta tugunlarni olasiz Values Scan.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

"Raqamli" qo'shimchalar ularni bir-biridan ajratishga yordam beradi - ular mos keladiganlar topilgan tartibda qo'shiladi. VALUES-so'rov bo'ylab yuqoridan pastgacha bloklaydi.

Ma'lumotni qayta ishlash

Bizning so'rovimizdagi hamma narsa hal qilinganga o'xshaydi - qolgani Limit.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Ammo bu erda hamma narsa oddiy - bunday tugunlar Limit, Sort, Aggregate, WindowAgg, Unique Agar ular mavjud bo'lsa, so'rovdagi tegishli operatorlarga birma-bir "xarita" qiling. Bu erda hech qanday "yulduzlar" yoki qiyinchiliklar yo'q.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

JOIN

Biz birlashmoqchi bo'lganimizda qiyinchiliklar paydo bo'ladi JOIN o'zaro. Bu har doim ham mumkin emas, lekin bu mumkin.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

So'rovni tahlil qilish nuqtai nazaridan bizda tugun mavjud JoinExpr, aynan ikkita farzandi bor - chap va o'ng. Shunga ko'ra, bu sizning JOIN-ning "yuqorida" va so'rovda "pastida" yozilgan narsadir.

Va reja nuqtai nazaridan, bu ba'zilarning ikkita avlodi * Loop/* Join-tugun. Nested Loop, Hash Anti Join,... - shunga o'xshash narsa.

Keling, oddiy mantiqdan foydalanamiz: agar bizda rejada bir-birini "birlashtirgan" A va B jadvallari bo'lsa, ular so'rovda joylashgan bo'lishi mumkin. A-JOIN-B, yoki B-JOIN-A. Keling, bu tarzda birlashtirishga harakat qilaylik, keling, boshqa yo'l bilan birlashtirishga harakat qilaylik va shunga o'xshash juftliklar tugamaguncha.

Keling, sintaksis daraxtimizni olaylik, rejamizni olamiz, ularga qaraymiz ... o'xshash emas!
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Keling, uni grafiklar shaklida qayta chizamiz - oh, u allaqachon biror narsaga o'xshaydi!
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Shuni ta'kidlaymizki, bizda bir vaqtning o'zida B va C bolalari bo'lgan tugunlar mavjud - biz qanday tartibda farq qilmaymiz. Keling, ularni birlashtiramiz va tugunning rasmini aylantiramiz.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Keling, yana qaraylik. Endi bizda A bolalari va juftliklari (B + C) bo'lgan tugunlar mavjud - ular bilan ham mos keladi.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Ajoyib! Ma'lum bo'lishicha, biz bu ikkimiz JOIN so'rovdan rejali tugunlar muvaffaqiyatli birlashtirildi.

Afsuski, bu muammo har doim ham hal etilmaydi.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Misol uchun, agar so'rovda bo'lsa A JOIN B JOIN C, va rejada, birinchi navbatda, "tashqi" A va C tugunlari ulangan.Ammo so'rovda bunday operator yo'q, bizda ta'kidlaydigan, ishora qo'shadigan hech narsa yo'q. Yozganingizda β€œvergul” bilan ham xuddi shunday A, B.

Ammo, aksariyat hollarda, deyarli barcha tugunlarni "echish" mumkin va siz o'z vaqtida chap tomonda bunday profilni olishingiz mumkin - tom ma'noda, JavaScript kodini tahlil qilganingizda Google Chrome kabi. Har bir satr va har bir bayonotning "bajarish" uchun qancha vaqt ketganini ko'rishingiz mumkin.
PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Va bularning barchasidan foydalanishni siz uchun qulayroq qilish uchun biz saqlash joyini yaratdik arxiv, bu erda siz o'zingizning rejalaringizni saqlashingiz va keyinchalik bog'liq so'rovlar bilan birga topishingiz yoki havolani kimgadir baham ko'rishingiz mumkin.

Agar siz shunchaki o'qib bo'lmaydigan so'rovni mos shaklga keltirmoqchi bo'lsangiz, foydalaning bizning "normalizatorimiz".

PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak

Manba: www.habr.com

a Izoh qo'shish