Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки

ΠŸΡ€Π΅Π΄ΠΈ няколко мСсСца обявихмС explain.tensor.ru β€” ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ сСрвиз Π·Π° Π°Π½Π°Π»ΠΈΠ· ΠΈ визуализация Π½Π° ΠΏΠ»Π°Π½ΠΎΠ²Π΅Ρ‚Π΅ Π½Π° заявкитС към PostgreSQL.

ΠŸΡ€Π΅Π· Ρ‚ΠΎΠ²Π° Π²Ρ€Π΅ΠΌΠ΅ Π²Π΅Ρ‡Π΅ стС Π³ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π»ΠΈ Π½Π°Π΄ 6000 ΠΏΡŠΡ‚ΠΈ, Π½ΠΎ Π΅Π΄Π½Π° ΠΎΡ‚ ΡƒΠ΄ΠΎΠ±Π½ΠΈΡ‚Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ вСроятно Π΅ останала нСзабСлязана β€” Ρ‚ΠΎΠ²Π° са структурнитС подсказки, ΠΊΠΎΠΈΡ‚ΠΎ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π°Ρ‚ ΠΏΡ€ΠΈΠ±Π»ΠΈΠ·ΠΈΡ‚Π΅Π»Π½ΠΎ Ρ‚Π°ΠΊΠ°:

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки

ΠŸΡ€ΠΈΡΠ»ΡƒΡˆΠ²Π°ΠΉΡ‚Π΅ сС към тях ΠΈ Π²Π°ΡˆΠΈΡ‚Π΅ заявки "Ρ‰Π΅ станат Π³Π»Π°Π΄ΠΊΠΈ ΠΈ ΠΊΠΎΠΏΡ€ΠΈΠ½Π΅Π½ΠΈ". πŸ™‚

А Π°ΠΊΠΎ бъдСм сСриозни, ΠΌΠ½ΠΎΠ³ΠΎ ситуации, ΠΊΠΎΠΈΡ‚ΠΎ правят заявката Π±Π°Π²Π½Π° ΠΈ "Π³Π»Π°Π΄Π½Π°" Π·Π° рСсурси, са Ρ‚ΠΈΠΏΠΈΡ‡Π½ΠΈ ΠΈ ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° Π±ΡŠΠ΄Π°Ρ‚ Ρ€Π°Π·ΠΏΠΎΠ·Π½Π°Ρ‚ΠΈ ΠΏΠΎ структурата ΠΈ Π΄Π°Π½Π½ΠΈΡ‚Π΅ Π½Π° ΠΏΠ»Π°Π½Π°.

Π’ Ρ‚ΠΎΠ·ΠΈ случай всСки ΠΎΡ‚Π΄Π΅Π»Π΅Π½ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ няма Π΄Π° трябва Π΄Π° Ρ‚ΡŠΡ€ΡΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ Π·Π° оптимизация самостоятСлно, Ρ€Π°Π·Ρ‡ΠΈΡ‚Π°ΠΉΠΊΠΈ СдинствСно Π½Π° своя ΠΎΠΏΠΈΡ‚ β€” ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° ΠΌΡƒ подсказвамС ΠΊΠ°ΠΊΠ²ΠΎ става, ΠΊΠ°ΠΊΠ²Π° ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π°Ρ‚Π° ΠΈ ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ΅ Π΄Π° сС ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈ към Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ. ΠšΠ°ΠΊΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅.

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки

НСка Π΄Π° Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΠΏΠΎ-ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ Ρ‚Π΅Π·ΠΈ случаи β€” ΠΊΠ°ΠΊ сС опрСдСлят ΠΈ към ΠΊΠ°ΠΊΠ²ΠΈ ΠΏΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ водят.

Π—Π° ΠΏΠΎ-Π΄ΠΎΠ±Ρ€ΠΎ потапянС Π² Ρ‚Π΅ΠΌΠ°Ρ‚Π° ΠΏΡŠΡ€Π²ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Ρ‡ΡƒΠ΅Ρ‚Π΅ ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ Π±Π»ΠΎΠΊ ΠΎΡ‚ ΠΌΠΎΠ΅Ρ‚ΠΎ ΠΈΠ·Π»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π½Π° PGConf.Russia 2020, Π° слСд Ρ‚ΠΎΠ²Π° Π΄Π° ΠΏΡ€Π΅ΠΌΠΈΠ½Π΅Ρ‚Π΅ към дСтайлния Π°Π½Π°Π»ΠΈΠ· Π½Π° всСки ΠΏΡ€ΠΈΠΌΠ΅Ρ€:

Π’ΡŠΠ·ΠΏΡ€ΠΎΠΈΠ·Π²Π΅Π΄ΠΈ Π²ΠΈΠ΄Π΅ΠΎ

#1: индСксная «нСдосортировка»

Кога възниква

ΠŸΠΎΠΊΠ°ΠΆΠ΅Ρ‚Π΅ послСдния ΠΎΡ‚Ρ‡Π΅Ρ‚ Π·Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° "ООО ΠšΠΎΠ»ΠΎΠΊΠΎΠ»ΡŒΡ‡ΠΈΠΊ".

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> Limit
   -> Sort
      -> Index [Only] Scan [Backward] | Bitmap Heap Scan

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Π˜Π·ΠΏΠΎΠ»Π·Π²Π°Π½ΠΈΡΡ‚ индСкс Π΄Π° сС Ρ€Π°Π·ΡˆΠΈΡ€ΠΈ с ΠΏΠΎΠ»Π΅Ρ‚Π° Π·Π° сортиранС.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk  -- 100K "Ρ„Π°ΠΊΡ‚ΠΈ"
, (random() * 1000)::integer fk_cli; -- 1K Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ външни ΠΊΠ»ΡŽΡ‡ΠΎΠ²Π΅

CREATE INDEX ON tbl(fk_cli); -- индСкс за foreign key

SELECT
  *
FROM
  tbl
WHERE
  fk_cli = 1 -- ΠΏΠΎΠ΄Π±ΠΎΡ€ ΠΏΠΎ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Π° Π²Ρ€ΡŠΠ·ΠΊΠ°
ORDER BY
  pk DESC -- искамС само Сдин "послСдСн" запис
LIMIT 1;

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

Π’Π΅Π΄Π½Π°Π³Π° Π΅ Π²ΠΈΠ΄Π½ΠΎ, Ρ‡Π΅ ΠΏΠΎ индСкса са ΠΈΠ·Π²Π°Π΄Π΅Π½ΠΈ Π½Π°Π΄ 100 записа, ΠΊΠΎΠΈΡ‚ΠΎ слСд Ρ‚ΠΎΠ²Π° са Π±ΠΈΠ»ΠΈ всички сортирани, Π° слСд Ρ‚ΠΎΠ²Π° Π΅ останал само Π΅Π΄ΠΈΠ½.

ΠŸΠΎΠΏΡ€Π°Π²ΡΠΌΠ΅:

DROP INDEX tbl_fk_cli_idx;
CREATE INDEX ON tbl(fk_cli, pk DESC); -- Π΄ΠΎΠ±Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΊΠ»ΡŽΡ‡ Π·Π° сортиранС

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

Π”ΠΎΡ€ΠΈ Π½Π° Ρ‚Π°ΠΊΠΎΠ²Π° простично ΠΈΠ·Π²Π°Π΄ΠΊΠ° β€” 8.5 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-Π±ΡŠΡ€Π·ΠΎ ΠΈ с 33 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ чСтСния.Π•Ρ„Π΅ΠΊΡ‚ΡŠΡ‚ Ρ‰Π΅ бъдС ΠΏΠΎ-ΠΎΡ‡Π΅Π²ΠΈΠ΄Π΅Π½, ΠΊΠΎΠ»ΠΊΠΎΡ‚ΠΎ ΠΏΠΎΠ²Π΅Ρ‡Π΅ "Ρ„Π°ΠΊΡ‚ΠΈ" ΠΈΠΌΠ°Ρ‚Π΅ Π·Π° всяко Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ fk.

ЗабСлязвам, Ρ‡Π΅ Ρ‚Π°ΠΊΡŠΠ² индСкс Ρ‰Π΅ Ρ€Π°Π±ΠΎΡ‚ΠΈ ΠΊΠ°Ρ‚ΠΎ "прСфиксСн" Π½Π΅ ΠΏΠΎ-лошо ΠΎΡ‚ ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½ΠΈΡ ΠΈ Π·Π° Π΄Ρ€ΡƒΠ³ΠΈ заявки с fk, ΠΊΡŠΠ΄Π΅Ρ‚ΠΎ сортировкитС ΠΏΠΎ pk Π½Π΅ Π΅ ΠΈΠΌΠ°Π»ΠΎ ΠΈ няма (ΠΏΠΎΠ²Π΅Ρ‡Π΅ Π·Π° Ρ‚ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Ρ‚Π΅ Π² моята статия Π·Π° Ρ‚ΡŠΡ€ΡΠ΅Π½Π΅ Π½Π° Π½Π΅Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΈ индСкси). Π’ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»Π½ΠΎ, Ρ‚ΠΎΠΉ Ρ‰Π΅ осигури ΠΈ Π½ΠΎΡ€ΠΌΠ°Π»Π½Π° ΠΏΠΎΠ΄Π΄Ρ€ΡŠΠΆΠΊΠ° Π½Π° явСн foreign key ΠΏΠΎ Ρ‚ΠΎΠ²Π° ΠΏΠΎΠ»Π΅.

#2: пСрСсСчСниС индСксов (BitmapAnd)

Кога възниква

ΠŸΠΎΠΊΠ°ΠΆΠ΅Ρ‚Π΅ всички Π΄ΠΎΠ³ΠΎΠ²ΠΎΡ€ΠΈ Π·Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° «ООО ΠšΠΎΠ»ΠΎΠΊΠΎΠ»ΡŒΡ‡ΠΈΠΊΒ», ΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈ ΠΎΡ‚ ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° «НАО Π›ΡŽΡ‚ΠΈΠΊΒ».

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> BitmapAnd
   -> Bitmap Index Scan
   -> Bitmap Index Scan

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

БъздаванС ΡΡŠΡΡ‚Π°Π²Π΅Π½ индСкс ΠΏΠΎ ΠΏΠΎΠ»Π΅Ρ‚Π°Ρ‚Π° ΠΎΡ‚ ΠΈ Π΄Π²Π΅Ρ‚Π΅ ΠΈΠ·Ρ…ΠΎΠ΄Π½ΠΈ, ΠΈΠ»ΠΈ Ρ€Π°Π·ΡˆΠΈΡ€Π΅Ρ‚Π΅ Π΅Π΄Π½ΠΎ ΠΎΡ‚ ΡΡŠΡ‰Π΅ΡΡ‚Π²ΡƒΠ²Π°Ρ‰ΠΈΡ‚Π΅ с ΠΏΠΎΠ»Π΅Ρ‚Π° ΠΎΡ‚ Π²Ρ‚ΠΎΡ€ΠΎΡ‚ΠΎ.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk      -- 100K "Ρ„Π°ΠΊΡ‚ΠΈ"
, (random() *  100)::integer fk_org  -- 100 Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ външни ΠΊΠ»ΡŽΡ‡ΠΎΠ²Π΅
, (random() * 1000)::integer fk_cli; -- 1K Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ външни ΠΊΠ»ΡŽΡ‡ΠΎΠ²Π΅

CREATE INDEX ON tbl(fk_org); -- индСкс за foreign key
CREATE INDEX ON tbl(fk_cli); -- индСкс за foreign key

SELECT
  *
FROM
  tbl
WHERE
  (fk_org, fk_cli) = (1, 999); -- ΠΎΡ‚Π±ΠΎΡ€ ΠΏΠΎ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Π° Π΄Π²ΠΎΠΉΠΊΠ°

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

ΠŸΠΎΠΏΡ€Π°Π²ΡΠΌΠ΅:

DROP INDEX tbl_fk_org_idx;
CREATE INDEX ON tbl(fk_org, fk_cli);

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

Π’ΡƒΠΊ ΠΏΠ΅Ρ‡Π°Π»Π±Π°Ρ‚Π° Π΅ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ Bitmap Heap Scan Π΅ Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΎ Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π΅Π½ сам ΠΏΠΎ сСбС си. Но всС ΠΏΠ°ΠΊ Π² 7 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-Π±ΡŠΡ€Π·ΠΎ ΠΈ с 2.5 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ чСтСния.

#3: объСдинСниС индСксов (BitmapOr)

Кога възниква

ΠŸΠΎΠΊΠ°ΠΆΠ΅Ρ‚Π΅ ΠΏΡŠΡ€Π²ΠΈΡ‚Π΅ 20 Π½Π°ΠΉ-стари «свои» ΠΈΠ»ΠΈ нСнасочСни заявки Π·Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°, ΠΊΠ°Ρ‚ΠΎ свои Π² ΠΏΡ€ΠΈΠΎΡ€ΠΈΡ‚Π΅Ρ‚.

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> BitmapOr
   -> Bitmap Index Scan
   -> Bitmap Index Scan

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Π”Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ UNION [ALL] Π·Π° обСдиняванС Π½Π° подзаявки ΠΏΠΎ всСки ΠΎΡ‚ OR-Π±Π»ΠΎΠΊΠΎΠ²Π΅Ρ‚Π΅ Π½Π° условията.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk  -- 100K "Ρ„Π°ΠΊΡ‚ΠΈ"
, CASE
    WHEN random() < 1::real/16 THEN NULL -- с вСроятност 1:16 запис "Π½ΠΈΡ‡ΠΈ"
    ELSE (random() * 100)::integer -- 100 Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ външни ΠΊΠ»ΡŽΡ‡ΠΎΠ²Π΅
  END fk_own;

CREATE INDEX ON tbl(fk_own, pk); -- индСкс с "Π²Ρ€ΠΎΠ΄Π΅ ΠΊΠ°ΠΊ подходяща" сортиранС

SELECT
  *
FROM
  tbl
WHERE
  fk_own = 1 OR -- свои
  fk_own IS NULL -- ... ΠΈΠ»ΠΈ "Π½ΠΈΡ‡ΠΈ"
ORDER BY
  pk
, (fk_own = 1) DESC -- ΠΏΡŠΡ€Π²ΠΎ "свои"
LIMIT 20;

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

ΠŸΠΎΠΏΡ€Π°Π²ΡΠΌΠ΅:

(
  SELECT
    *
  FROM
    tbl
  WHERE
    fk_own = 1 -- ΠΏΡŠΡ€Π²ΠΎ "свои" 20
  ORDER BY
    pk
  LIMIT 20
)
UNION ALL
(
  SELECT
    *
  FROM
    tbl
  WHERE
    fk_own IS NULL -- слСд Ρ‚ΠΎΠ²Π° "Π½ΠΈΡ‡ΠΈ" 20
  ORDER BY
    pk
  LIMIT 20
)
LIMIT 20; -- Π½ΠΎ ΠΎΠ±Ρ‰ΠΎ - 20, ΠΏΠΎΠ²Π΅Ρ‡Π΅ ΠΈ Π½Π΅ Π΅ Π½ΡƒΠΆΠ½ΠΎ

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

Π’ΡŠΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ…ΠΌΠ΅ сС ΠΎΡ‚ Ρ‚ΠΎΠ²Π°, Ρ‡Π΅ всичкитС 20 Π½ΡƒΠΆΠ½ΠΈ записа бяха Π²Π΅Π΄Π½Π°Π³Π° ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈ Π² ΠΏΡŠΡ€Π²ΠΈΡ Π±Π»ΠΎΠΊ, ΠΏΠΎΡ€Π°Π΄ΠΈ ΠΊΠΎΠ΅Ρ‚ΠΎ вторият, с ΠΏΠΎ-«скъп» Bitmap Heap Scan, Π΄ΠΎΡ€ΠΈ Π½Π΅ бСшС изпълнСн β€” Π² Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ Π² 22 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-Π±ΡŠΡ€Π·ΠΎ, Π² 44 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ чСтСния!

По-ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π΅Π½ Ρ€Π°Π·ΠΊΠ°Π· Π·Π° Ρ‚ΠΎΠ·ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄ Π½Π° оптимизация Π½Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΈ ΠΌΠΎΠΆΠ΅ Π΄Π° сС ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅ Π² статиитС PostgreSQL Antipatterns: Π²Ρ€Π΅Π΄Π½ΠΈ JOIN ΠΈ OR ΠΈ PostgreSQL Antipatterns: ΠΏΡ€ΠΈΠΊΠ°Π·ΠΊΠ° Π·Π° ΠΈΡ‚Π΅Ρ€Π°Ρ‚ΠΈΠ²Π½ΠΎΡ‚ΠΎ Π΄ΠΎΡ€Π°Π±ΠΎΡ‚Π²Π°Π½Π΅ Π½Π° Ρ‚ΡŠΡ€ΡΠ΅Π½Π΅Ρ‚ΠΎ ΠΏΠΎ Π½Π°ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅, ΠΈΠ»ΠΈ Β«ΠžΠΏΡ‚ΠΈΠΌΠΈΠ·Π°Ρ†ΠΈΡ Π½Π°ΠΏΡ€Π΅Π΄ ΠΈ Π½Π°Π·Π°Π΄Β».

ΠžΠ±ΠΎΠ±Ρ‰Π΅Π½ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ Π½Π° ΠΏΠΎΠ΄Ρ€Π΅Π΄Π΅Π½ ΠΎΡ‚Π±ΠΎΡ€ ΠΏΠΎ няколко ΠΊΠ»ΡŽΡ‡Π° (Π° Π½Π΅ само ΠΏΠΎ Π΄Π²ΠΎΠΉΠΊΠ° const/NULL) Ρ€Π°Π·Π³Π»Π΅Π΄Π°Π½ Π² статията SQL HowTo: пишСм while-Ρ†ΠΈΠΊΡŠΠ» Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎ Π² Π·Π°ΠΏΠΈΡ‚Π²Π°Π½Π΅Ρ‚ΠΎ, ΠΈΠ»ΠΈ "Π•Π»Π΅ΠΌΠ΅Π½Ρ‚Π°Ρ€Π½Π° Ρ‚Ρ€ΠΈΡΡŠΡΡ‚Π°Π²ΠΊΠ°".

#4: Ρ‡ΠΈΡ‚Π°Π΅ΠΌ ΠΌΠ½ΠΎΠ³ΠΎ лишнСго

Кога възниква

ОбикновСно възниква ΠΏΡ€ΠΈ ΠΆΠ΅Π»Π°Π½ΠΈΠ΅ Π΄Π° сС Β«ΠΏΡ€ΠΈΠΊΡ€ΡƒΡ‚ΠΈΒ» ΠΎΡ‰Π΅ Π΅Π΄ΠΈΠ½ Ρ„ΠΈΠ»Ρ‚ΡŠΡ€ към Π²Π΅Ρ‡Π΅ ΡΡŠΡ‰Π΅ΡΡ‚Π²ΡƒΠ²Π°Ρ‰Π° заявка.

«А няматС Π»ΠΈ Ρ‚Π°ΠΊΠΎΠ²Π°, Π½ΠΎ с ΠΏΠ΅Ρ€Π»Π΅Π½ΠΈ ΠΊΠΎΠΏΡ‡Π΅Ρ‚Π°?Β» Ρ…/Ρ„ «Брилянтова Ρ€ΡŠΠΊΠ°Β»

НапримСр, ΠΌΠΎΠ΄ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΉΠΊΠΈ Π·Π°Π΄Π°Ρ‡Π°Ρ‚Π° ΠΏΠΎ-Π³ΠΎΡ€Π΅, ΠΏΠΎΠΊΠ°ΠΆΠ΅Ρ‚Π΅ ΠΏΡŠΡ€Π²ΠΈΡ‚Π΅ 20 Π½Π°ΠΉ-стари Β«ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½ΠΈΒ» заявки Π·Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°, нСзависимо ΠΎΡ‚ тСхния статус.

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> Seq Scan | Bitmap Heap Scan | Index [Only] Scan [Backward]
   && 5 Γ— rows 80% ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½Π½ΠΎΠ³ΠΎ
   && loops Γ— RRbF > 100 -- ΠΈ ΠΏΡ€ΠΈ этом большС 100 записСй суммарно

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Π‘ΡŠΠ·Π΄Π°ΠΉΡ‚Π΅ [ΠΏΠΎ-спСциализиран] индСкс с WHERE-условиС ΠΈΠ»ΠΈ Π΄ΠΎΠ±Π°Π²Π΅Ρ‚Π΅ Π΄ΠΎΠΏΡŠΠ»Π½ΠΈΡ‚Π΅Π»Π½ΠΈ ΠΏΠΎΠ»Π΅Ρ‚Π° Π² индСкса.

Ако условиСто Π·Π° Ρ„ΠΈΠ»Ρ‚Ρ€ΠΈΡ€Π°Π½Π΅ Π΅ "статично" Π·Π° Π²Π°ΡˆΠΈΡ‚Π΅ Π·Π°Π΄Π°Ρ‡ΠΈ β€” Ρ‚.Π΅. Π½Π΅ ΠΏΡ€Π΅Π΄Π²ΠΈΠΆΠ΄Π° Ρ€Π°Π·ΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ Π½Π° списъка ΠΎΡ‚ стойности Π² Π±ΡŠΠ΄Π΅Ρ‰Π΅ β€” ΠΏΠΎ-Π΄ΠΎΠ±Ρ€Π΅ Π΅ Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° WHERE-индСкс. Π’ Ρ‚Π°Π·ΠΈ катСгория сС вписват Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ boolean/enum-статуси.

Ако всС ΠΏΠ°ΠΊ условиСто Π·Π° Ρ„ΠΈΠ»Ρ‚Ρ€ΠΈΡ€Π°Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΏΡ€ΠΈΠ΅ΠΌΠ° Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ стойности, Ρ‚ΠΎ ΠΏΠΎ-Π΄ΠΎΠ±Ρ€Π΅ Π΄Π° Ρ€Π°Π·ΡˆΠΈΡ€ΠΈΡ‚Π΅ индСкса с Ρ‚Π΅Π·ΠΈ ΠΏΠΎΠ»Π΅Ρ‚Π° β€” ΠΊΠ°ΠΊΡ‚ΠΎ Π² ситуацията с BitmapAnd ΠΏΠΎ-Π³ΠΎΡ€Π΅.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

Π‘ΠͺΠ—Π”ΠΠ”Π˜ Π‘Π’ΠžΠ› tbl AS
Π˜Π—Π‘Π•Π Π˜
  generate_series(1, 100000) pk -- 100K "Ρ„Π°ΠΊΡ‚ΠΈ"
, БЛУЧАЙ
    ΠšΠžΠ“ΠΠ’Πž random() < 1::real/16 Π’ΠžΠ“ΠΠ’Π NULL
    Π˜ΠΠΠ§Π• (random() * 100)::integer -- 100 Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ външни ΠΊΠ»ΡŽΡ‡Π°
  ΠšΠ ΠΠ™ fk_own
, (random() < 1::real/50) critical; -- 1:50, Ρ‡Π΅ заявката Π΅ "ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½Π°"

Π‘ΠͺЗДАЙ Π˜ΠΠ”Π•ΠšΠ‘ НА tbl(pk);
Π‘ΠͺЗДАЙ Π˜ΠΠ”Π•ΠšΠ‘ НА tbl(fk_own, pk);

Π˜Π—Π‘Π•Π Π˜
  *
ОВ
  tbl
КΠͺΠ”Π•
  critical
ΠΠΠ Π•Π”Π˜ ПО
  pk
ΠžΠ“Π ΠΠΠ˜Π§Π˜ 20;

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

ΠŸΠΎΠΏΡ€Π°Π²ΡΠΌΠ΅:

Π‘ΠͺЗДАЙ Π˜ΠΠ”Π•ΠšΠ‘ НА tbl(pk)
  КΠͺΠ”Π• critical; -- Π΄ΠΎΠ±Π°Π²ΠΈΡ…ΠΌΠ΅ "статично" условиС Π·Π° Ρ„ΠΈΠ»Ρ‚Ρ€ΠΈΡ€Π°Π½Π΅

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

ΠšΠ°ΠΊΡ‚ΠΎ Π²ΠΈΠΆΠ΄Π°ΠΌΠ΅, Ρ„ΠΈΠ»Ρ‚Ρ€ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ ΠΎΡ‚ ΠΏΠ»Π°Π½Π° напълно ΠΈΠ·Ρ‡Π΅Π·Π½Π°, Π° заявката стана Π² 5 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-Π±ΡŠΡ€Π·Π°.

#5: разрСТСнная Ρ‚Π°Π±Π»ΠΈΡ†Π°

Кога възниква

Π Π°Π·Π½ΠΎΠΎΠ±Ρ€Π°Π·Π½ΠΈ ΠΎΠΏΠΈΡ‚ΠΈ Π·Π° създаванС Π½Π° собствСна опашка Π·Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° Π·Π°Π΄Π°Ρ‡ΠΈ, ΠΊΠΎΠ³Π°Ρ‚ΠΎ голям Π±Ρ€ΠΎΠΉ Π°ΠΊΡ‚ΡƒΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ/изтривания Π½Π° записи Π² Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° водят Π΄ΠΎ ситуация с ΠΌΠ½ΠΎΠ³ΠΎ "ΠΌΡŠΡ€Ρ‚Π²ΠΈ" записи.

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> Seq Scan | Bitmap Heap Scan | Index [Only] Scan [Backward]
   && loops Γ— (rows + RRbF)  64

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Π Π΅Π΄ΠΎΠ²Π½ΠΎ Ρ€ΡŠΡ‡Π½ΠΎ ΠΏΡ€ΠΎΠ²Π΅ΠΆΠ΄Π°Π½Π΅ Π½Π° VACUUM [FULL] ΠΈΠ»ΠΈ достиганС Π½Π° Π°Π΄Π΅ΠΊΠ²Π°Ρ‚Π½ΠΎ чСсто изпълнСниС autovacuum Ρ‡Ρ€Π΅Π· ΠΏΡ€Π΅Ρ†ΠΈΠ·Π½ΠΎ настройванС Π½Π° Π½Π΅Π³ΠΎΠ²ΠΈΡ‚Π΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΈ, Π² Ρ‚ΠΎΠ²Π° число Π·Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Π°Ρ‚Π° Ρ‚Π°Π±Π»ΠΈΡ†Π°.

Π’ ΠΏΠΎΠ²Π΅Ρ‡Π΅Ρ‚ΠΎ случаи ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΈ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ сС ΠΎΠΊΠ°Π·Π²Π°Ρ‚ ΠΏΡ€Π΅Π΄ΠΈΠ·Π²ΠΈΠΊΠ°Π½ΠΈ ΠΎΡ‚ лошо ΠΊΠΎΠΌΠΏΠΎΠ½ΠΈΡ€Π°Π½Π΅ Π½Π° заявки ΠΏΡ€ΠΈ повиквания с бизнСс Π»ΠΎΠ³ΠΈΠΊΠ° ΠΊΠ°Ρ‚ΠΎ Ρ‚Π΅Π·ΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ бяха обсъдСни Π² PostgreSQL АнтипатСрни: Π±ΠΎΡ€Π±Π° с ΠΎΡ€Π΄ΠΈΡ‚Π΅ Π½Π° β€žΠΌΡŠΡ€Ρ‚Π²ΠΈΡ‚Π΅β€œ..

Но трябва Π΄Π° сС Ρ€Π°Π·Π±Π΅Ρ€Π΅, Ρ‡Π΅ Π΄ΠΎΡ€ΠΈ VACUUM FULL Π½Π΅ ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΏΠΎΠΌΠΎΠ³Π½Π΅ Π²ΠΈΠ½Π°Π³ΠΈ. Π—Π° Ρ‚Π°ΠΊΠΈΠ²Π° случаи Π΅ Π΄ΠΎΠ±Ρ€Π΅ Π΄Π° сС Π·Π°ΠΏΠΎΠ·Π½Π°Π΅Ρ‚Π΅ с Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΡŠΠΌΠ° ΠΎΡ‚ статията DBA: ΠΊΠΎΠ³Π°Ρ‚ΠΎ VACUUM Π½Π΅ ΠΏΠΎΠΌΠ°Π³Π° β€” чистим Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° Ρ€ΡŠΡ‡Π½ΠΎ.

#6: Ρ‡Ρ‚Π΅Π½ΠΈΠ΅ с «сСрСдины» индСкса

Кога възниква

ИзглСТда, Ρ‡Π΅ смС ΠΏΡ€ΠΎΡ‡Π΅Π»ΠΈ ΠΌΠ°Π»ΠΊΠΎ, всичко Π΅ ΠΏΠΎ индСкса ΠΈ Π½ΠΈΠΊΠΎΠ³ΠΎ Π½Π΅Π½ΡƒΠΆΠ½ΠΎ Π½Π΅ смС Ρ„ΠΈΠ»Ρ‚Ρ€ΠΈΡ€Π°Π»ΠΈ β€” Π° всС ΠΏΠ°ΠΊ ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Π½ΠΈΡ‚Π΅ страници са Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»Π½ΠΎ ΠΏΠΎΠ²Π΅Ρ‡Π΅, ΠΎΡ‚ΠΊΠΎΠ»ΠΊΠΎΡ‚ΠΎ Π±ΠΈΡ…ΠΌΠ΅ ΠΆΠ΅Π»Π°Π»ΠΈ.

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> Index [Only] Scan [Backward]
   && loops Γ— (rows + RRbF)  64

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Π’Π½ΠΈΠΌΠ°Ρ‚Π΅Π»Π½ΠΎ Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΉΡ‚Π΅ структурата Π½Π° използвания индСкс ΠΈ ΠΊΠ»ΡŽΡ‡ΠΎΠ²ΠΈΡ‚Π΅ ΠΏΠΎΠ»Π΅Ρ‚Π°, Π·Π°Π΄Π°Π΄Π΅Π½ΠΈ Π² Π·Π°ΠΏΠΈΡ‚Π²Π°Π½Π΅Ρ‚ΠΎ - вСроятно, част ΠΎΡ‚ индСкса Π½Π΅ Π΅ Π·Π°Π΄Π°Π΄Π΅Π½Π°. ВСроятно Ρ‰Π΅ трябва Π΄Π° ΡΡŠΠ·Π΄Π°Π΄Π΅Ρ‚Π΅ ΠΏΠΎΠ΄ΠΎΠ±Π΅Π½ индСкс, Π½ΠΎ Π±Π΅Π· прСфиксни ΠΏΠΎΠ»Π΅Ρ‚Π° ΠΈΠ»ΠΈ Π΄Π° Π½Π°ΡƒΡ‡ΠΈΡ‚Π΅ ΠΊΠ°ΠΊ Π΄Π° ΠΈΡ‚Π΅Ρ€ΠΈΡ€Π°Ρ‚Π΅ Ρ‚Π΅Ρ…Π½ΠΈΡ‚Π΅ стойности.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

CREATE TABLE tbl AS
SELECT
  generate_series(1, 100000) pk      -- 100K "Ρ„Π°ΠΊΡ‚ΠΈ"
, (random() *  100)::integer fk_org  -- 100 Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ външни ΠΊΠ»ΡŽΡ‡Π°
, (random() * 1000)::integer fk_cli; -- 1K Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ външни ΠΊΠ»ΡŽΡ‡Π°

CREATE INDEX ON tbl(fk_org, fk_cli); -- всичко ΠΏΠΎΡ‡Ρ‚ΠΈ ΠΊΠ°ΠΊΡ‚ΠΎ Π² #2
-- само Ρ‡Π΅ отдСлният индСкс ΠΏΠΎ fk_cli Π²Π΅Ρ‡Π΅ Π³ΠΎ смСтнахмС Π·Π° излишСн ΠΈ Π³ΠΎ ΠΏΡ€Π΅ΠΌΠ°Ρ…Π½Π°Ρ…ΠΌΠ΅

SELECT
  *
FROM
  tbl
WHERE
  fk_cli = 999 -- Π° fk_org Π½Π΅ Π΅ Π·Π°Π΄Π°Π΄Π΅Π½, Π²ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‡Π΅ стои ΠΏΠΎ-Ρ€Π°Π½ΠΎ Π² индСкса
LIMIT 20;

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

ИзглСТда всичко Π΅ Π½Π°Ρ€Π΅Π΄, Π΄ΠΎΡ€ΠΈ ΠΏΠΎ индСкса, Π½ΠΎ ΠΏΠΎ някакъв Π½Π°Ρ‡ΠΈΠ½ Π΅ ΠΏΠΎΠ΄ΠΎΠ·Ρ€ΠΈΡ‚Π΅Π»Π½ΠΎ - Π½Π° всяка ΠΎΡ‚ 20-Ρ‚Π΅ ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅Π½ΠΈ записи сС Π½Π°Π»ΠΎΠΆΠΈ Π΄Π° сС ΠΈΠ·Π²Π»Π΅ΠΊΠ°Ρ‚ ΠΏΠΎ 4 страници Π΄Π°Π½Π½ΠΈ, 32KB Π½Π° запис - Π½Π΅ Π΅ Π»ΠΈ Ρ‚Π²ΡŠΡ€Π΄Π΅ ΠΌΠ½ΠΎΠ³ΠΎ? И ΠΈΠΌΠ΅Ρ‚ΠΎ Π½Π° индСкса tbl_fk_org_fk_cli_idx ΠΏΡ€Π΅Π΄ΠΈΠ·Π²ΠΈΠΊΠ²Π° размисли.

ΠŸΠΎΠΏΡ€Π°Π²ΡΠΌΠ΅:

CREATE INDEX ON tbl(fk_cli);

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

НСочаквано - 10 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-Π±ΡŠΡ€Π·ΠΎ, ΠΈ 4 ΠΏΡŠΡ‚ΠΈ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ Ρ‡Π΅Ρ‚Π΅Π½Π΅!

Π”Ρ€ΡƒΠ³ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΈ Π·Π° ситуации с Π½Π΅Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° индСкси ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Π²ΠΈΠ΄ΠΈΡ‚Π΅ Π² статията DBA: ΠΎΡ‚ΠΊΡ€ΠΈΠ²Π°ΠΌΠ΅ Π±Π΅Π·ΠΏΠΎΠ»Π΅Π·Π½ΠΈ индСкси.

#7: CTE Γ— CTE

Кога възниква

Π’ Π·Π°ΠΏΠΈΡ‚Π²Π°Π½Π΅Ρ‚ΠΎ ΡΡŠΠ±Ρ€Π°Ρ…ΠΌΠ΅ "Π΄Π΅Π±Π΅Π»ΠΈ" CTE ΠΎΡ‚ Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ Ρ‚Π°Π±Π»ΠΈΡ†ΠΈ, Π° слСд Ρ‚ΠΎΠ²Π° Ρ€Π΅ΡˆΠΈΡ…ΠΌΠ΅ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΌΠ΅ΠΆΠ΄Ρƒ тях JOIN.

Блучаят Π΅ Π°ΠΊΡ‚ΡƒΠ°Π»Π΅Π½ Π·Π° вСрсии ΠΏΠΎΠ΄ v12 ΠΈΠ»ΠΈ запитвания с WITH MATERIALIZED.

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> CTE Scan
   && Ρ†ΠΈΠΊΠ»ΠΈ > 10
   && Ρ†ΠΈΠΊΠ»ΠΈ Γ— (Ρ€Π΅Π΄ΠΎΠ²Π΅ + RRbF) > 10000
      -- Ρ‚Π²ΡŠΡ€Π΄Π΅ голямо Π΄Π΅ΠΊΠ°Ρ€Ρ‚ΠΎΠ²ΠΎ ΠΏΡ€ΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΈΠ΅ CTE

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Π’Π½ΠΈΠΌΠ°Ρ‚Π΅Π»Π½ΠΎ Π°Π½Π°Π»ΠΈΠ·ΠΈΡ€Π°ΠΉΡ‚Π΅ Π·Π°ΠΏΠΈΡ‚Π²Π°Π½Π΅Ρ‚ΠΎ - Π° Π½ΡƒΠΆΠ½ΠΈ Π»ΠΈ са ΠΈΠ·ΠΎΠ±Ρ‰ΠΎ CTE Ρ‚ΡƒΠΊ? Если всС-Ρ‚Π°ΠΊΠΈ Π΄Π°, Ρ‚ΠΎ Π΄Π° ΠΏΡ€ΠΈΠ»ΠΎΠΆΠΈΡ‚Π΅ "ословорянС" Π² hstore/json ΠΏΠΎ ΠΌΠΎΠ΄Π΅Π»Π°, описан Π² PostgreSQL АнтипатСрни: ΠΏΠ΅Ρ‡Π°Ρ‚ΠΈΠΌΠ΅ Π·Π±ΠΎΡ€Π½ΠΈΠΊΠΎΡ‚ ΠΏΡ€ΠΎΡ‚ΠΈΠ² Ρ‚Π΅ΡˆΠΊΠΈΡ‚Π΅ JOIN.

#8: swap на диск (temp written)

Кога възниква

Π•Π΄Π½ΠΎΠΊΡ€Π°Ρ‚Π½Π°Ρ‚Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° (сортировка ΠΈΠ»ΠΈ уникализация) Π½Π° голямо количСство записи Π½Π΅ сС ΠΏΠΎΠ±ΠΈΡ€Π° Π² ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π°Ρ‚Π° Π·Π° Ρ‚ΠΎΠ²Π° ΠΏΠ°ΠΌΠ΅Ρ‚.

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> *
   && temp written > 0

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Ако ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½ΠΎΡ‚ΠΎ ΠΎΡ‚ опСрацията количСство ΠΏΠ°ΠΌΠ΅Ρ‚ Π½Π΅ надвишава Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»Π½ΠΎ установСната стойност Π½Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚ΡŠΡ€Π° work_mem, струва си Π΄Π° Π³ΠΎ ΠΊΠΎΡ€ΠΈΠ³ΠΈΡ€Π°Ρ‚Π΅. МоТС Π΄Π° сС Π½Π°ΠΏΡ€Π°Π²ΠΈ Π²Π΅Π΄Π½Π°Π³Π° Π² конфигурацията Π·Π° всички, ΠΈΠ»ΠΈ Ρ‡Ρ€Π΅Π· SET [LOCAL] Π·Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎ Π·Π°ΠΏΠΈΡ‚Π²Π°Π½Π΅/транзакция.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

SHOW work_mem;
-- "16MB"

SELECT
  random()
FROM
  generate_series(1, 1000000)
ORDER BY
  1;

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

ΠŸΠΎΠΏΡ€Π°Π²ΡΠΌΠ΅:

SET work_mem = '128MB'; -- ΠΏΡ€Π΅Π΄ΠΈ ΠΈΠ·ΠΏΡŠΠ»Π½Π΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½Π° Π·Π°ΠΏΠΈΡ‚Π²Π°Π½Π΅Ρ‚ΠΎ

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
ΠšΠ°ΠΊΡ‚ΠΎ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Ρ…ΠΌΠ΅, Π½Π°ΠΌΠ΅Ρ€ΠΈΡ…ΠΌΠ΅ всички 30 записа. Но ΠΎΡ‚Π½Π΅ 60% ΠΎΡ‚ Π²Ρ€Π΅ΠΌΠ΅Ρ‚ΠΎ β€” Π·Π°Ρ‰ΠΎΡ‚ΠΎ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ…ΠΌΠ΅ ΠΈ 30 Ρ‚ΡŠΡ€ΡΠ΅Π½ΠΈΡ ΠΏΠΎ индСкса. А ΠΌΠΎΠΆΠ΅ΠΌ Π»ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ?

По Ρ€Π°Π·Π±ΠΈΡ€Π°Π΅ΠΌΠΈ ΠΏΡ€ΠΈΡ‡ΠΈΠ½ΠΈ, Π°ΠΊΠΎ сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° само ΠΏΠ°ΠΌΠ΅Ρ‚, Π° Π½Π΅ диск, Π·Π°ΠΏΠΈΡ‚Π²Π°Π½Π΅Ρ‚ΠΎ ΡΡŠΡ‰ΠΎ Ρ‰Π΅ сС изпълнява ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎ-Π±ΡŠΡ€Π·ΠΎ. Π’ ΡΡŠΡ‰ΠΎΡ‚ΠΎ Π²Ρ€Π΅ΠΌΠ΅ част ΠΎΡ‚ Π½Π°Ρ‚ΠΎΠ²Π°Ρ€Π²Π°Π½Π΅Ρ‚ΠΎ ΠΎΡ‚ HDD сС отстранява.

Но трябва Π΄Π° Ρ€Π°Π·Π±Π΅Ρ€Π΅Ρ‚Π΅, Ρ‡Π΅ Π½Π΅ ΠΌΠΎΠΆΠ΅ Π²ΠΈΠ½Π°Π³ΠΈ Π΄Π° сС отдСля ΠΌΠ½ΠΎΠ³ΠΎ-ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠ°ΠΌΠ΅Ρ‚ - просто няма Π΄Π° стигнС Π·Π° всички.

#9: Π½Π΅Π°ΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½Π°Ρ статистика

Кога възниква

Π’ Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ бяха Π²ΠΊΠ°Ρ€Π°Π½ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ записи, Π½ΠΎ Π½Π΅ успяха Π΄Π° ΠΏΡ€Π΅ΠΌΠΈΠ½Π°Ρ‚ ANALYZE.

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> Seq Scan | Bitmap Heap Scan | Index [Only] Scan [Backward]
   && ratio >> 10

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Π”Π° ΠΏΡ€ΠΎΠ²Π΅Π΄Π΅ΠΌ всС ΠΏΠ°ΠΊ ANALYZE.

По-ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ, ситуация Π΅ описана Π² PostgreSQL Antipatterns: статистиката Π΅ всичко.

#10: Β«Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ пошло Π½Π΅ Ρ‚Π°ΠΊΒ»

Кога възниква

Π‘Π»ΡƒΡ‡ΠΈ сС Ρ‡Π°ΠΊΠ°Π½Π΅ Π½Π° Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠ°, Π½Π°Π»ΠΎΠΆΠ΅Π½Π° ΠΎΡ‚ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π½Π° заявка, ΠΈΠ»ΠΈ липсваха Ρ…Π°Ρ€Π΄ΡƒΠ΅Ρ€Π½ΠΈ рСсурси CPU/Ρ…ΠΈΠΏΠ΅Ρ€Π²ΠΈΠ·ΠΎΡ€.

Как Π΄Π° ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΡ†ΠΈΡ€Π°ΠΌΠ΅

-> *
   && (shared hit / 8K) + (shared read / 1K) < time / 1000
      -- RAM hit = 64MB/s, HDD read = 8MB/s
   && time > 100ms -- Ρ‡Π΅Ρ‚Π΅Π½Π΅Ρ‚ΠΎ бСшС ΠΌΠ°Π»ΠΊΠΎ, Π½ΠΎ ΠΎΡ‚Π½Π΅ Ρ‚Π²ΡŠΡ€Π΄Π΅ ΠΌΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅

ΠŸΡ€Π΅ΠΏΠΎΡ€ΡŠΠΊΠΈ

Π˜Π·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ външна систСма Π·Π° ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ Π½Π° ΡΡŠΡ€Π²ΡŠΡ€ΠΈ Π·Π° Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ Π½Π° Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ ΠΈΠ»ΠΈ Π½Π΅Π½ΠΎΡ€ΠΌΠ°Π»Π½ΠΎ ΠΏΠΎΡ‚Ρ€Π΅Π±Π»Π΅Π½ΠΈΠ΅ Π½Π° рСсурси. Π—Π° нашия ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ към ΠΎΡ€Π³Π°Π½ΠΈΠ·ΠΈΡ€Π°Π½Π΅ Π½Π° Ρ‚ΠΎΠ·ΠΈ процСс Π·Π° стотици ΡΡŠΡ€Π²ΡŠΡ€ΠΈ Π²Π΅Ρ‡Π΅ Π³ΠΎΠ²ΠΎΡ€ΠΈΡ…ΠΌΠ΅ Ρ‚ΡƒΠΊ ΠΈ Ρ‚ΡƒΠΊ.

Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки
Π Π΅Ρ†Π΅ΠΏΡ‚ΠΈ Π·Π° Π±ΠΎΠ»Π΅Π΄ΡƒΠ²Π°Ρ‰ΠΈ SQL заявки

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС със Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS ΠΈ VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ πŸ”₯ ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС със Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS ΠΈ VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ | ProHoster