Զգուշացեք բուֆերներ բերող գործողություններից...
Օգտագործելով փոքրիկ հարցումը որպես օրինակ՝ եկեք դիտարկենք PostgreSQL-ում հարցումների օպտիմալացման մի քանի ունիվերսալ մոտեցումներ: Օգտագործեք դրանք, թե ոչ, կախված է ձեզանից, բայց արժե իմանալ դրանց մասին:
PG-ի որոշ հետագա տարբերակներում իրավիճակը կարող է փոխվել, քանի որ ժամանակացույցը դառնում է ավելի խելացի, բայց 9.4/9.6-ի համար այն մոտավորապես նույնն է թվում, ինչպես այստեղ ներկայացված օրինակներում:
Եկեք մի շատ իրական խնդրանք ընդունենք.
SELECT
TRUE
FROM
"Документ" d
INNER JOIN
"ДокументРасширение" doc_ex
USING("@Документ")
INNER JOIN
"ТипДокумента" t_doc ON
t_doc."@ТипДокумента" = d."ТипДокумента"
WHERE
(d."Лицо3" = 19091 or d."Сотрудник" = 19091) AND
d."$Черновик" IS NULL AND
d."Удален" IS NOT TRUE AND
doc_ex."Состояние"[1] IS TRUE AND
t_doc."ТипДокумента" = 'ПланРабот'
LIMIT 1;
աղյուսակների և դաշտերի անունների մասինԴաշտերի և աղյուսակների «ռուսական» անվանումներին կարելի է այլ կերպ վերաբերվել, բայց դա ճաշակի հարց է։ Քանի որ
Դիտարկենք արդյունքում ստացված պլանը.
144 ms և գրեթե 53K բուֆերներ - այսինքն ավելի քան 400 ՄԲ տվյալ: Եվ մեր բախտը կբերի, եթե դրանք բոլորը լինեն քեշում մինչև մեր խնդրանքը, հակառակ դեպքում սկավառակից կարդալիս շատ անգամ ավելի երկար կպահանջվի:
Ալգորիթմն ամենակարևորն է:
Ցանկացած հարցում ինչ-որ կերպ օպտիմալացնելու համար նախ պետք է հասկանալ, թե ինչ պետք է անի:
Եկեք առայժմ թողնենք տվյալների բազայի կառուցվածքի զարգացումը այս հոդվածի շրջանակներից դուրս և համաձայնենք, որ մենք կարող ենք համեմատաբար «էժան»: վերաշարադրել հարցումը և/կամ գլորում ենք հիմքի վրա մեզ անհրաժեշտ որոշ իրեր Ինդեքսը.
Այսպիսով, խնդրանքը.
— ստուգում է առնվազն որոշ փաստաթղթի առկայությունը
- մեզ անհրաժեշտ վիճակում և որոշակի տեսակի
- որտեղ հեղինակը կամ կատարողը մեզ անհրաժեշտ աշխատողն է
ՄԻԱՑԵՔ + ՍԱՀՄԱՆԱՓԱԿ 1
Հաճախ ծրագրավորողի համար ավելի հեշտ է հարցում գրել, որտեղ սկզբում միացվում են մեծ թվով աղյուսակներ, իսկ հետո այս ամբողջ հավաքածուից մնում է միայն մեկ գրառում: Սակայն ծրագրավորողի համար ավելի հեշտ չի նշանակում տվյալների բազայի համար ավելի արդյունավետ:
Մեր դեպքում կար ընդամենը 3 աղյուսակ, և ի՞նչ էֆեկտ...
Եկեք նախ ձերբազատվենք «Փաստաթղթի տեսակ» աղյուսակի հետ կապից և միևնույն ժամանակ շտեմարանին ասենք, որ. մեր տեսակի ռեկորդը եզակի է (մենք գիտենք սա, բայց ժամանակացույցը դեռ գաղափար չունի).
WITH T AS (
SELECT
"@ТипДокумента"
FROM
"ТипДокумента"
WHERE
"ТипДокумента" = 'ПланРабот'
LIMIT 1
)
...
WHERE
d."ТипДокумента" = (TABLE T)
...
Այո, եթե աղյուսակը/CTE-ը բաղկացած է մեկ գրառման մեկ դաշտից, ապա PG-ում կարող եք նույնիսկ գրել այսպես՝ փոխարենը.
d."ТипДокумента" = (SELECT "@ТипДокумента" FROM T LIMIT 1)
Ծույլ գնահատում PostgreSQL հարցումներում
BitmapOr ընդդեմ UNION
Որոշ դեպքերում, Bitmap Heap Scan-ը մեզ շատ կարժենա, օրինակ՝ մեր իրավիճակում, երբ բավականին շատ գրառումներ համապատասխանում են պահանջվող պայմանին: Մենք դա ստացանք, քանի որ ԿԱՄ վիճակը վերածվեց BitmapOr-ի- շահագործումը պլանում.
Վերադառնանք բուն խնդրին. մենք պետք է համապատասխան գրառում գտնենք ցանկացածին պայմաններից, այսինքն՝ կարիք չկա որոնել բոլոր 59K գրառումները երկու պայմաններում: Մեկ պայման մշակելու միջոց կա, և գնալ երկրորդին միայն այն ժամանակ, երբ առաջինում ոչինչ չի հայտնաբերվել. Հետևյալ դիզայնը կօգնի մեզ.
(
SELECT
...
LIMIT 1
)
UNION ALL
(
SELECT
...
LIMIT 1
)
LIMIT 1
«Արտաքին» LIMIT 1-ը ապահովում է, որ որոնումն ավարտվի, երբ գտնվի առաջին գրառումը: Եվ եթե այն արդեն հայտնաբերվել է առաջին բլոկում, ապա երկրորդ բլոկը չի կատարվի (երբեք չի կատարվել առնչությամբ).
«Դժվար պայմանները ՔԱՂԱՔՈՒՄ»
Բնօրինակ հարցման մեջ չափազանց անհարմար պահ կա՝ «DocumentExtension» համապատասխան աղյուսակի հետ կարգավիճակի ստուգում: Անկախ արտահայտության այլ պայմանների ճշմարտացիությունից (օրինակ. դ. «Ջնջվածը» ՃԻՇՏ ՉԻ), այս կապը միշտ իրականացվում է և «ծախսում է ռեսուրսները»: Դրանցից քիչ թե շատ կծախսվի - կախված է այս աղյուսակի չափից:
Բայց դուք կարող եք փոփոխել հարցումը, որպեսզի համապատասխան գրառումների որոնումը տեղի ունենա միայն այն դեպքում, երբ դա իսկապես անհրաժեշտ է.
SELECT
...
FROM
"Документ" d
WHERE
... /*index cond*/ AND
CASE
WHEN "$Черновик" IS NULL AND "Удален" IS NOT TRUE THEN (
SELECT
"Состояние"[1] IS TRUE
FROM
"ДокументРасширение"
WHERE
"@Документ" = d."@Документ"
)
END
Մի անգամ կապված աղյուսակից մեզ Արդյունքի համար ոչ մի դաշտ պետք չէ, այնուհետև մենք հնարավորություն ունենք JOIN-ը վերածել պայմանի ենթահարցման:
Եկեք թողնենք ինդեքսավորված դաշտերը «CASE փակագծերից դուրս», գրառումից պարզ պայմաններ ավելացնենք WHEN բլոկին, և այժմ «ծանր» հարցումը կատարվում է միայն THEN-ին անցնելիս:
Իմ ազգանունն է «Ընդամենը»
Ստացված հարցումը մենք հավաքում ենք վերը նկարագրված բոլոր մեխանիզմներով.
WITH T AS (
SELECT
"@ТипДокумента"
FROM
"ТипДокумента"
WHERE
"ТипДокумента" = 'ПланРабот'
)
(
SELECT
TRUE
FROM
"Документ" d
WHERE
("Лицо3", "ТипДокумента") = (19091, (TABLE T)) AND
CASE
WHEN "$Черновик" IS NULL AND "Удален" IS NOT TRUE THEN (
SELECT
"Состояние"[1] IS TRUE
FROM
"ДокументРасширение"
WHERE
"@Документ" = d."@Документ"
)
END
LIMIT 1
)
UNION ALL
(
SELECT
TRUE
FROM
"Документ" d
WHERE
("ТипДокумента", "Сотрудник") = ((TABLE T), 19091) AND
CASE
WHEN "$Черновик" IS NULL AND "Удален" IS NOT TRUE THEN (
SELECT
"Состояние"[1] IS TRUE
FROM
"ДокументРасширение"
WHERE
"@Документ" = d."@Документ"
)
END
LIMIT 1
)
LIMIT 1;
ինդեքսների ճշգրտում
Մարզված աչքը նկատեց, որ UNION ենթաբլոկների ինդեքսավորման պայմանները մի փոքր տարբերվում են, քանի որ մենք արդեն ունենք համապատասխան ինդեքսներ սեղանին: Եվ եթե դրանք չլինեին, արժեր ստեղծել. Փաստաթուղթ (անձ 3, փաստաթղթի տեսակ) и Փաստաթուղթ (Փաստաթղթի տեսակ, աշխատող).
ROW պայմաններում դաշտերի հերթականության մասինՊլանավորողի տեսանկյունից, իհարկե, կարող եք գրել (A, B) = (constA, constB)Իսկ (B, A) = (constB, constA). Բայց ձայնագրելիս ինդեքսի դաշտերի հերթականությամբ, նման հարցումը պարզապես ավելի հարմար է ավելի ուշ կարգաբերելու համար։
Ի՞նչ կա պլանում:
Ցավոք սրտի, մեր բախտը չբերեց, և առաջին ՀԱՄԱԽՄԲՈՒՄ ոչինչ չգտնվեց, ուստի երկրորդը դեռ մահապատժի ենթարկվեց։ Բայց նույնիսկ այդպես - միայն 0.037ms և 11 բուֆեր!
Մենք արագացրել ենք հարցումը և կրճատել տվյալների պոմպային հիշողության մեջ մի քանի հազար անգամ, բավականին պարզ տեխնիկայի կիրառմամբ՝ լավ արդյունք մի փոքր copy-paste-ով։ 🙂
Source: www.habr.com