Π Π΅Ρ†Π΅ΠΏΡ‚Ρ‹ для Ρ…Π²ΠΎΡ€Π°ΡŽΡ‰ΠΈΡ… 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-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π‘Ρ€Π°Π·Ρƒ ΠΌΠΎΠΆΠ½ΠΎ Π·Π°ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ ΠΏΠΎ индСксу Π²Ρ‹Ρ‡ΠΈΡ‚Π°Π»ΠΎΡΡŒ большС 100 записСй, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΡ‚ΠΎΠΌ всС ΡΠΎΡ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π»ΠΈΡΡŒ, Π° ΠΏΠΎΡ‚ΠΎΠΌ Π±Ρ‹Π»Π° оставлСна СдинствСнная.

Π˜ΡΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ:

DROP INDEX tbl_fk_cli_idx;
CREATE INDEX ON tbl(fk_cli, pk DESC); -- Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ ΠΊΠ»ΡŽΡ‡ сортировки

Π Π΅Ρ†Π΅ΠΏΡ‚Ρ‹ для Ρ…Π²ΠΎΡ€Π°ΡŽΡ‰ΠΈΡ… SQL-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π”Π°ΠΆΠ΅ Π½Π° Ρ‚Π°ΠΊΠΎΠΉ ΠΏΡ€ΠΈΠΌΠΈΡ‚ΠΈΠ²Π½ΠΎΠΉ Π²Ρ‹Π±ΠΎΡ€ΠΊΠ΅ β€” Π² 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-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π˜ΡΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ:

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

Π Π΅Ρ†Π΅ΠΏΡ‚Ρ‹ для Ρ…Π²ΠΎΡ€Π°ΡŽΡ‰ΠΈΡ… SQL-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π’ΡƒΡ‚ Π²Ρ‹ΠΈΠ³Ρ€Ρ‹Ρˆ мСньшС, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ 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-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π˜ΡΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ:

(
  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-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

ΠœΡ‹ воспользовались Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ всС 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 < RRbF -- ΠΎΡ‚Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠ²Π°Π½ΠΎ >80% ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½Π½ΠΎΠ³ΠΎ
   && loops Γ— RRbF > 100 -- ΠΈ ΠΏΡ€ΠΈ этом большС 100 записСй суммарно

Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΠΈ

Π‘ΠΎΠ·Π΄Π°Ρ‚ΡŒ [Π±ΠΎΠ»Π΅Π΅] спСциализированный индСкс с WHERE-условиСм ΠΈΠ»ΠΈ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Π² индСкс Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ поля.

Если условиС Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΠΈ являСтся «статичным» для Π²Π°ΡˆΠΈΡ… Π·Π°Π΄Π°Ρ‡ β€” Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ Π½Π΅ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Π΅Ρ‚ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ пСрСчня Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ Π² Π±ΡƒΠ΄ΡƒΡ‰Π΅ΠΌ β€” Π»ΡƒΡ‡ΡˆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ WHERE-индСкс. Π’ эту ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΡŽ Ρ…ΠΎΡ€ΠΎΡˆΠΎ ΡƒΠΊΠ»Π°Π΄Ρ‹Π²Π°ΡŽΡ‚ΡΡ Ρ€Π°Π·Π½Ρ‹Π΅ boolean/enum-статусы.

Если ΠΆΠ΅ условиС Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΠΈ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Ρ‚ΡŒ Ρ€Π°Π·Π½Ρ‹Π΅ значСния, Ρ‚ΠΎ Π»ΡƒΡ‡ΡˆΠ΅ Ρ€Π°ΡΡˆΠΈΡ€ΠΈΡ‚ΡŒ индСкс этими полями β€” ΠΊΠ°ΠΊ Π² ситуации с BitmapAnd Π²Ρ‹ΡˆΠ΅.

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

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

CREATE INDEX ON tbl(pk);
CREATE INDEX ON tbl(fk_own, pk);

SELECT
  *
FROM
  tbl
WHERE
  critical
ORDER BY
  pk
LIMIT 20;

Π Π΅Ρ†Π΅ΠΏΡ‚Ρ‹ для Ρ…Π²ΠΎΡ€Π°ΡŽΡ‰ΠΈΡ… SQL-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π˜ΡΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ:

CREATE INDEX ON tbl(pk)
  WHERE critical; -- Π΄ΠΎΠ±Π°Π²ΠΈΠ»ΠΈ "статичноС" условиС Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΠΈ

Π Π΅Ρ†Π΅ΠΏΡ‚Ρ‹ для Ρ…Π²ΠΎΡ€Π°ΡŽΡ‰ΠΈΡ… SQL-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Как Π²ΠΈΠ΄ΠΈΠΌ, Ρ„ΠΈΠ»ΡŒΡ‚Ρ€Π°Ρ†ΠΈΡ ΠΈΠ· ΠΏΠ»Π°Π½Π° ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΡƒΡˆΠ»Π°, Π° запрос стал Π² 5 Ρ€Π°Π· быстрСС.

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

Когда Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ‚

Π Π°Π·Π½ΠΎΠΎΠ±Ρ€Π°Π·Π½Ρ‹Π΅ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠΈ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΡΠΎΠ±ΡΡ‚Π²Π΅Π½Π½ΡƒΡŽ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π·Π°Π΄Π°Ρ‡, ΠΊΠΎΠ³Π΄Π° большоС количСство ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠΉ/ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠΉ записСй Π½Π° Ρ‚Π°Π±Π»ΠΈΡ†Π΅ приводят ΠΊ ситуации большого количСства Β«ΠΌΠ΅Ρ€Ρ‚Π²Ρ‹Ρ…Β» записСй.

Как ΠΎΠΏΠΎΠ·Π½Π°Ρ‚ΡŒ

-> Seq Scan | Bitmap Heap Scan | Index [Only] Scan [Backward]
   && loops Γ— (rows + RRbF) < (shared hit + shared read) Γ— 8
      -- ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½ΠΎ большС 1KB Π½Π° ΠΊΠ°ΠΆΠ΄ΡƒΡŽ запись
   && shared hit + shared read > 64

Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΠΈ

РСгулярно Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ ΠΏΡ€ΠΎΠ²ΠΎΠ΄ΠΈΡ‚ΡŒ VACUUM [FULL] ΠΈΠ»ΠΈ Π΄ΠΎΠ±ΠΈΡ‚ΡŒΡΡ Π°Π΄Π΅ΠΊΠ²Π°Ρ‚Π½ΠΎ частой ΠΎΡ‚Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ autovacuum с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Ρ‚ΠΎΠ½ΠΊΠΎΠΉ настройки Π΅Π³ΠΎ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ², Π² Ρ‚ΠΎΠΌ числС для ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠΉ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹.

Π’ Π±ΠΎΠ»ΡŒΡˆΠΈΠ½ΡΡ‚Π²Π΅ случаСв ΠΏΠΎΠ΄ΠΎΠ±Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ ΠΎΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‚ΡΡ Π²Ρ‹Π·Π²Π°Π½Ρ‹ ΠΏΠ»ΠΎΡ…ΠΎΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½ΠΎΠ²ΠΊΠΎΠΉ запросов ΠΏΡ€ΠΈ Π²Ρ‹Π·ΠΎΠ²Π°Ρ… с бизнСс-Π»ΠΎΠ³ΠΈΠΊΠΈ Π²Ρ€ΠΎΠ΄Π΅ Ρ‚Π΅Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±Ρ‹Π»ΠΈ рассмотрСны Π² PostgreSQL Antipatterns: сраТаСмся с ΠΎΡ€Π΄Π°ΠΌΠΈ Β«ΠΌΠ΅Ρ€Ρ‚Π²Π΅Ρ†ΠΎΠ²Β».

Но Π½Π°Π΄ΠΎ ΠΏΠΎΠ½ΠΈΠΌΠ°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎ Π΄Π°ΠΆΠ΅ VACUUM FULL ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠΌΠΎΡ‡ΡŒ Π½Π΅ всСгда. Для Ρ‚Π°ΠΊΠΈΡ… случаСв стоит ΠΎΠ·Π½Π°ΠΊΠΎΠΌΠΈΡ‚ΡŒΡΡ с Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠΌ ΠΈΠ· ΡΡ‚Π°Ρ‚ΡŒΠΈ DBA: ΠΊΠΎΠ³Π΄Π° пасуСт VACUUM β€” чистим Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ.

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

Когда Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ‚

Π’Ρ€ΠΎΠ΄Π΅ ΠΈ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π»ΠΈ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ, ΠΈ всС ΠΏΠΎ индСксу, ΠΈ Π½ΠΈΠΊΠΎΠ³ΠΎ лишнСго Π½Π΅ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠ²Π°Π»ΠΈ β€” Π° всС Ρ€Π°Π²Π½ΠΎ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½ΠΎ сущСствСнно большС страниц, Ρ‡Π΅ΠΌ Ρ…ΠΎΡ‚Π΅Π»ΠΎΡΡŒ Π±Ρ‹.

Как ΠΎΠΏΠΎΠ·Π½Π°Ρ‚ΡŒ

-> Index [Only] Scan [Backward]
   && loops Γ— (rows + RRbF) < (shared hit + shared read) Γ— 8
      -- ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½ΠΎ большС 1KB Π½Π° ΠΊΠ°ΠΆΠ΄ΡƒΡŽ запись
   && shared hit + shared read > 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-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π’Ρ€ΠΎΠ΄Π΅ Π±Ρ‹ всС Ρ…ΠΎΡ€ΠΎΡˆΠΎ, Π΄Π°ΠΆΠ΅ ΠΏΠΎ индСксу, Π½ΠΎ ΠΊΠ°ΠΊ-Ρ‚ΠΎ ΠΏΠΎΠ΄ΠΎΠ·Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ β€” Π½Π° ΠΊΠ°ΠΆΠ΄ΡƒΡŽ ΠΈΠ· 20 ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π½Π½Ρ‹Ρ… записСй ΠΏΡ€ΠΈΡˆΠ»ΠΎΡΡŒ Π²Ρ‹Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ ΠΏΠΎ 4 страницы Π΄Π°Π½Π½Ρ‹Ρ…, 32KB Π½Π° запись β€” Π½Π΅ ΠΆΠΈΡ€Π½ΠΎ Π»ΠΈ? Π”Π° ΠΈ имя индСкса tbl_fk_org_fk_cli_idx Π½Π°Π²ΠΎΠ΄ΠΈΡ‚ Π½Π° Ρ€Π°Π·ΠΌΡ‹ΡˆΠ»Π΅Π½ΠΈΡ.

Π˜ΡΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ:

CREATE INDEX ON tbl(fk_cli);

Π Π΅Ρ†Π΅ΠΏΡ‚Ρ‹ для Ρ…Π²ΠΎΡ€Π°ΡŽΡ‰ΠΈΡ… SQL-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π’Π½Π΅Π·Π°ΠΏΠ½ΠΎ β€” Π² 10 Ρ€Π°Π· быстрСС, ΠΈ Π² 4 Ρ€Π°Π·Π° мСньшС Ρ‡ΠΈΡ‚Π°Ρ‚ΡŒ!

Π”Ρ€ΡƒΠ³ΠΈΠ΅ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ ситуаций нСэффСктивного использования индСксов ΠΌΠΎΠΆΠ½ΠΎ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ Π² ΡΡ‚Π°Ρ‚ΡŒΠ΅ DBA: Π½Π°Ρ…ΠΎΠ΄ΠΈΠΌ бСсполСзныС индСксы.

#7: CTE Γ— CTE

Когда Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ‚

Π’ запросС Π½Π°Π±Ρ€Π°Π»ΠΈ Β«ΠΆΠΈΡ€Π½Ρ‹Ρ…Β» CTE ΠΈΠ· Ρ€Π°Π·Π½Ρ‹Ρ… Ρ‚Π°Π±Π»ΠΈΡ†, Π° ΠΏΠΎΡ‚ΠΎΠΌ Ρ€Π΅ΡˆΠΈΠ»ΠΈ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΌΠ΅ΠΆΠ΄Ρƒ Π½ΠΈΠΌΠΈ JOIN.

КСйс Π°ΠΊΡ‚ΡƒΠ°Π»Π΅Π½ для вСрсий Π½ΠΈΠΆΠ΅ v12 ΠΈΠ»ΠΈ запросов с WITH MATERIALIZED.

Как ΠΎΠΏΠΎΠ·Π½Π°Ρ‚ΡŒ

-> CTE Scan
   && loops > 10
   && loops Γ— (rows + RRbF) > 10000
      -- слишком большоС Π΄Π΅ΠΊΠ°Ρ€Ρ‚ΠΎΠ²ΠΎ ΠΏΡ€ΠΎΠΈΠ·Π²Π΅Π΄Π΅Π½ΠΈΠ΅ CTE

Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΠΈ

Π’Π½ΠΈΠΌΠ°Ρ‚Π΅Π»ΡŒΠ½ΠΎ ΠΏΡ€ΠΎΠ°Π½Π°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ запрос β€” Π° Π½ΡƒΠΆΠ½Ρ‹ Π»ΠΈ Ρ‚ΡƒΡ‚ Π²ΠΎΠΎΠ±Ρ‰Π΅ CTE? Если всС-Ρ‚Π°ΠΊΠΈ Π΄Π°, Ρ‚ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ «ословариваниС» Π² hstore/json ΠΏΠΎ ΠΌΠΎΠ΄Π΅Π»ΠΈ, описанной Π² PostgreSQL Antipatterns: ΡƒΠ΄Π°Ρ€ΠΈΠΌ словарСм ΠΏΠΎ тяТСлому 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-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

Π˜ΡΠΏΡ€Π°Π²Π»ΡΠ΅ΠΌ:

SET work_mem = '128MB'; -- ΠΏΠ΅Ρ€Π΅Π΄ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ΠΌ запроса

Π Π΅Ρ†Π΅ΠΏΡ‚Ρ‹ для Ρ…Π²ΠΎΡ€Π°ΡŽΡ‰ΠΈΡ… SQL-запросов
[ΠΏΠΎΡΠΌΠΎΡ‚Ρ€Π΅Ρ‚ΡŒ Π½Π° explain.tensor.ru]

По понятным ΠΏΡ€ΠΈΡ‡ΠΈΠ½Π°ΠΌ, Ссли ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠ°ΠΌΡΡ‚ΡŒ, Π° Π½Π΅ диск, Ρ‚ΠΎ ΠΈ запрос Π±ΡƒΠ΄Π΅Ρ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒΡΡ Π½Π°ΠΌΠ½ΠΎΠ³ΠΎ быстрСС. ΠŸΡ€ΠΈ этом Π΅Ρ‰Π΅ ΠΈ Ρ‡Π°ΡΡ‚ΡŒ Π½Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ с 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