ProHoster > Blog > Ma'muriyat > PostgreSQL Query Profiler: reja va so'rovni qanday moslashtirish kerak
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...
... mos reja tugunlari uchun kontekstli maslahatlar bilan chiroyli tarzda ishlab chiqilgan so'rovga:
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.
Ayniqsa, ishlab chiquvchilar koddagi so'rovning tanasini "yopishtirganda" (bu, albatta, antipattern, lekin shunday bo'ladi) bir qatorda. Dahshatli!
Keling, buni qandaydir chiroyliroq chizamiz.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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
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.
"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.
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.
JOIN
Biz birlashmoqchi bo'lganimizda qiyinchiliklar paydo bo'ladi JOIN o'zaro. Bu har doim ham mumkin emas, lekin bu mumkin.
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, uni grafiklar shaklida qayta chizamiz - oh, u allaqachon biror narsaga o'xshaydi!
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.
Keling, yana qaraylik. Endi bizda A bolalari va juftliklari (B + C) bo'lgan tugunlar mavjud - ular bilan ham mos keladi.
Ajoyib! Ma'lum bo'lishicha, biz bu ikkimiz JOIN so'rovdan rejali tugunlar muvaffaqiyatli birlashtirildi.
Afsuski, bu muammo har doim ham hal etilmaydi.
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.
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".