ืžืชื›ื•ื ื™ื ืœืฉืื™ืœืชื•ืช SQL ื—ื•ืœื•ืช

ืœืคื ื™ ืžืกืคืจ ื—ื•ื“ืฉื™ื ื”ื›ืจื–ื ื• explain.tensor.ru - ืฆื™ื‘ื•ืจื™ ืฉื™ืจื•ืช ืœื ื™ืชื•ื— ื•ื”ืฆื’ื” ืฉืœ ืชื•ื›ื ื™ื•ืช ืฉืื™ืœืชื•ืช ืœ PostgreSQL.

ื”ืฉืชืžืฉืช ื‘ื• ื™ื•ืชืจ ืž-6000 ืคืขืžื™ื ืžืื–, ืื‘ืœ ืื—ืช ื”ืชื›ื•ื ื•ืช ื”ืฉื™ืžื•ืฉื™ื•ืช ืื•ืœื™ ื ืขืœืžื• ืžืขื™ื ื™ื”ื ื”ื™ื ืจืžื–ื™ื ืžื‘ื ื™ื™ื, ืฉื ืจืื™ื ื‘ืขืจืš ื›ืš:

ืžืชื›ื•ื ื™ื ืœืฉืื™ืœืชื•ืช SQL ื—ื•ืœื•ืช

ื”ืงืฉื™ื‘ื• ืœื”ื ื•ื”ื‘ืงืฉื•ืช ืฉืœื›ื "ื™ื”ืคื›ื• ื—ืœืงื•ืช ื›ืžืฉื™". ๐Ÿ™‚

ืื‘ืœ ื‘ืจืฆื™ื ื•ืช, ืžืฆื‘ื™ื ืจื‘ื™ื ืฉื”ื•ืคื›ื™ื ื‘ืงืฉื” ืœืื˜ื™ืช ื•"ื’ืจื’ืจื ื™ืช" ืžื‘ื—ื™ื ืช ืžืฉืื‘ื™ื, ืื•ืคื™ื™ื ื™ื™ื ื•ื ื™ืชืŸ ืœื–ื”ื•ืชื ืœืคื™ ื”ืžื‘ื ื” ื•ื”ื ืชื•ื ื™ื ืฉืœ ื”ืชื•ื›ื ื™ืช.

ื‘ืžืงืจื” ื–ื”, ื›ืœ ืžืคืชื— ืื™ื ื“ื™ื‘ื™ื“ื•ืืœื™ ืœื ื™ืฆื˜ืจืš ืœื—ืคืฉ ื‘ืขืฆืžื• ืืคืฉืจื•ืช ืื•ืคื˜ื™ืžื™ื–ืฆื™ื”, ื‘ื”ืกืชืžืš ืจืง ืขืœ ื”ื ื™ืกื™ื•ืŸ ืฉืœื• - ื ื•ื›ืœ ืœืกืคืจ ืœื• ืžื” ืงื•ืจื” ื›ืืŸ, ืžื” ื™ื›ื•ืœื” ืœื”ื™ื•ืช ื”ืกื™ื‘ื”, ื•ื›ืŸ ืื™ืš ืœื”ืžืฆื™ื ืคืชืจื•ืŸ. ื•ื–ื” ืžื” ืฉืขืฉื™ื ื•.

ืžืชื›ื•ื ื™ื ืœืฉืื™ืœืชื•ืช SQL ื—ื•ืœื•ืช

ื‘ื•ืื• ื ืกืชื›ืœ ืžืงืจื•ื‘ ืขืœ ื”ืžืงืจื™ื ื”ืœืœื• โ€“ ื›ื™ืฆื“ ื”ื ืžื•ื’ื“ืจื™ื ื•ืœืื™ืœื• ื”ืžืœืฆื•ืช ื”ื ืžื•ื‘ื™ืœื™ื.

ื›ื“ื™ ืœื”ืชืขืžืง ื˜ื•ื‘ ื™ื•ืชืจ ื‘ื ื•ืฉื, ืืชื” ื™ื›ื•ืœ ืงื•ื“ื ืœื”ืื–ื™ืŸ ืœื‘ืœื•ืง ื”ืžืชืื™ื ืž ื”ื“ื™ื•ื•ื— ืฉืœื™ ื‘-PGConf.Russia 2020, ื•ืจืง ืื– ืขื‘ื•ืจ ืœื ื™ืชื•ื— ืžืคื•ืจื˜ ืฉืœ ื›ืœ ื“ื•ื’ืžื”:

ืžืก' 1: ืื™ื ื“ืงืก "ืžื™ื•ืŸ"

ืžืชื™ ืขื•ืœื”

ื”ืฆื’ ืืช ื”ื—ืฉื‘ื•ื ื™ืช ื”ืื—ืจื•ื ื” ืขื‘ื•ืจ ื”ืœืงื•ื— "LLC Kolokolchik".

ืื™ืš ืœื–ื”ื•ืช

-> 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 ืœื ื”ื™ื” ื•ืœื ื”ื™ื” (ืชื•ื›ืœ ืœืงืจื•ื ืขื•ื“ ืขืœ ื–ื” ื‘ืžืืžืจ ืฉืœื™ ืขืœ ืžืฆื™ืืช ืื™ื ื“ืงืกื™ื ืœื ื™ืขื™ืœื™ื). ื‘ืคืจื˜, ื–ื” ื™ืกืคืง ื ื•ืจืžืœื™ ืชืžื™ื›ืช ืžืคืชื— ื–ืจ ืžืคื•ืจืฉืช ืœืคื™ ื”ืฉื“ื” ื”ื–ื”.

#2: ืฆื•ืžืช ืื™ื ื“ืงืก (BitmapAnd)

ืžืชื™ ืขื•ืœื”

ื”ืฆื’ ืืช ื›ืœ ื”ื—ื•ื–ื™ื ืขื‘ื•ืจ ื”ืœืงื•ื— "LLC Kolokolchik" ืฉื ื—ืชื ืžื˜ืขื "NJSC Lyutik".

ืื™ืš ืœื–ื”ื•ืช

-> 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 [ื›ืœ] ื›ื“ื™ ืœืฉืœื‘ ืฉืื™ืœืชื•ืช ืžืฉื ื” ืขื‘ื•ืจ ื›ืœ ืื—ื“ ืžื‘ืœื•ืง ื”ืชื ืื™ 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 "ื”ื™ืงืจื”" ื™ื•ืชืจ, ืืคื™ืœื• ืœื ื‘ื•ืฆืขื” - ื›ืชื•ืฆืื” ืžื›ืš ืคื™ 22 ืžื”ืจ ื™ื•ืชืจ, ืคื™ 44 ืคื—ื•ืช ืงืจื™ืื•ืช!

ืกื™ืคื•ืจ ืžืคื•ืจื˜ ื™ื•ืชืจ ืขืœ ืฉื™ื˜ืช ื”ืื•ืคื˜ื™ืžื™ื–ืฆื™ื” ื”ื–ื• ืขืœ ื“ื•ื’ืžืื•ืช ืงื•ื ืงืจื˜ื™ื•ืช ื ื™ืชืŸ ืœืงืจื•ื ื‘ืžืืžืจื™ื PostgreSQL Antipatterns: JOINs ื•-ORs ืžื–ื™ืงื™ื ะธ 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. ืกื˜ื˜ื•ืกื™ื ืฉื•ื ื™ื ืฉืœ ื‘ื•ืœื™ืืŸ/ื ืžืฆื ืžืชืื™ืžื™ื ื”ื™ื˜ื‘ ืœืงื˜ื’ื•ืจื™ื” ื–ื•.

ืื ืžืฆื‘ ื”ืกื™ื ื•ืŸ ื™ื›ื•ืœ ืœืงื‘ืœ ืขืจื›ื™ื ืฉื•ื ื™ื, ืขื“ื™ืฃ ืœื”ืจื—ื™ื‘ ืืช ื”ืื™ื ื“ืงืก ืขื ื”ืฉื“ื•ืช ื”ืืœื” - ื›ืžื• ื‘ืžืฆื‘ ืฉืœ 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

ื”ืžืœืฆื•ืช

ื‘ืฆืข ื‘ืื•ืคืŸ ื™ื“ื ื™ ื‘ืื•ืคืŸ ืงื‘ื•ืข ื•ืืงื•ื [ืžืœื] ืื• ืœื”ืฉื™ื’ ืขื™ื‘ื•ื“ ืชื›ื•ืฃ ืžืกืคื™ืง ื•ืืงื•ื ืื•ื˜ื•ืžื˜ื™ ืขืœ ื™ื“ื™ ื›ื•ื•ื ื•ืŸ ืขื“ื™ืŸ ืฉืœ ื”ืคืจืžื˜ืจื™ื ืฉืœื•, ื›ื•ืœืœ ืขื‘ื•ืจ ื˜ื‘ืœื” ืกืคืฆื™ืคื™ืช.

ื‘ืจื•ื‘ ื”ืžืงืจื™ื, ื‘ืขื™ื•ืช ืืœื• ื ื’ืจืžื•ืช ืขืœ ื™ื“ื™ ืคืจื™ืกืช ืฉืื™ืœืชื” ืœืงื•ื™ื” ื›ืืฉืจ ื ืงืจืื•ืช ืžื”ื™ื’ื™ื•ืŸ ืขืกืงื™, ื›ื’ื•ืŸ ืืœื• ืฉื ื“ื•ื ื• ื‘ PostgreSQL Antipatterns: ื ืœื—ืžื™ื ื‘ื”ืžื•ื ื™ "ืžืชื™ื".

ืื‘ืœ ืขืœื™ื ื• ืœื”ื‘ื™ืŸ ืฉืืคื™ืœื• ื•ืืงื•ื ืžืœื ืœื ืชืžื™ื“ ื™ื›ื•ืœ ืœืขื–ื•ืจ. ื‘ืžืงืจื™ื ื›ืืœื”, ื›ื“ืื™ ืœื”ื›ื™ืจ ืืช ื”ืืœื’ื•ืจื™ืชื ืžื”ืžืืžืจ. DBA: ื›ืืฉืจ ื•ืืงื•ื ืขื•ื‘ืจ, ืื ื• ืžื ืงื™ื ืืช ื”ืฉื•ืœื—ืŸ ื‘ืื•ืคืŸ ื™ื“ื ื™.

#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: Dictionary Hit Heavy JOIN.

#8: ื”ื—ืœืคื” ืœื“ื™ืกืง (ื›ืชื•ื‘ืช ื–ืžื ื™ืช)

ืžืชื™ ืขื•ืœื”

ืขื™ื‘ื•ื“ ื—ื“ ืคืขืžื™ (ืžื™ื•ืŸ ืื• ื™ื™ื—ื•ื“) ืฉืœ ืžืกืคืจ ืจื‘ ืฉืœ ืจืฉื•ืžื•ืช ืื™ื ื• ืžืชืื™ื ืœื–ื™ื›ืจื•ืŸ ืฉื”ื•ืงืฆื” ืœื›ืš.

ืื™ืš ืœื–ื”ื•ืช

-> *
   && 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]

ืžืกื™ื‘ื•ืช ื‘ืจื•ืจื•ืช, ืื ืžืฉืชืžืฉื™ื ืจืง ื‘ื–ื™ื›ืจื•ืŸ ื•ืœื ื‘ื“ื™ืกืง, ื”ืฉืื™ืœืชื” ืชื”ื™ื” ื”ืจื‘ื” ื™ื•ืชืจ ืžื”ื™ืจื”. ื‘ืžืงื‘ื™ืœ, ื—ืœืง ืžื”ืขื•ืžืก ืžื•ืกืจ ื’ื ืžื”ื“ื™ืกืง ื”ืงืฉื™ื—.

ืื‘ืœ ืื ื—ื ื• ื—ื™ื™ื‘ื™ื ืœื”ื‘ื™ืŸ ืฉืœื ืชืžื™ื“ ื™ืขื‘ื•ื“ ื’ื ืœื”ืงืฆื•ืช ื”ืจื‘ื” ื–ื™ื›ืจื•ืŸ - ื–ื” ื ื“ื•ืฉ ืœื ืžืกืคื™ืง ืœื›ื•ืœื.

#9: ืกื˜ื˜ื™ืกื˜ื™ืงื” ืœื ืจืœื•ื•ื ื˜ื™ืช

ืžืชื™ ืขื•ืœื”

ื”ืจื‘ื” ื ืฉืคืš ืœื‘ืกื™ืก ื‘ื‘ืช ืื—ืช, ืื‘ืœ ืœื ื”ื™ื” ืœื”ื ื–ืžืŸ ืœื’ืจืฉ ืื•ืชื• ANALYZE.

ืื™ืš ืœื–ื”ื•ืช

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

ื”ืžืœืฆื•ืช

ืชื•ืฆื™ื ืื•ืชื• ื“ื‘ืจ ANALYZE.

ืžืฆื‘ ื–ื” ืžืชื•ืืจ ื‘ื™ืชืจ ืคื™ืจื•ื˜ ื‘ PostgreSQL Antipatterns: ื”ืกื˜ื˜ื™ืกื˜ื™ืงื” ื”ื™ื ื”ืจืืฉ ืฉืœ ื”ื›ืœ.

#10: "ืžืฉื”ื• ื”ืฉืชื‘ืฉ"

ืžืชื™ ืขื•ืœื”

ื”ื™ื™ืชื” ื ืขื™ืœื” ืฉื”ืžืชื™ื ื” ืœื‘ืงืฉื” ืžืชื—ืจื”, ืื• ืฉืœื ื”ื™ื• ืžืกืคื™ืง ืžืฉืื‘ื™ ื—ื•ืžืจื” ืฉืœ CPU/Hypervisor.

ืื™ืš ืœื–ื”ื•ืช

-> *
   && (shared hit / 8K) + (shared read / 1K) < time / 1000
      -- RAM hit = 64MB/s, HDD read = 8MB/s
   && time > 100ms -- ั‡ะธั‚ะฐะปะธ ะผะฐะปะพ, ะฝะพ ัะปะธัˆะบะพะผ ะดะพะปะณะพ

ื”ืžืœืฆื•ืช

ื”ืฉืชืžืฉ ื—ื™ืฆื•ื ื™ ืžืขืจื›ืช ื ื™ื˜ื•ืจ ืฉืจืช ืœื—ืกื™ืžื” ืื• ืฆืจื™ื›ืช ืžืฉืื‘ื™ื ื—ืจื™ื’ื”. ื›ื‘ืจ ื“ื™ื‘ืจื ื• ืขืœ ื”ื’ืจืกื” ืฉืœื ื• ืœืืจื’ื•ืŸ ื”ืชื”ืœื™ืš ื”ื–ื” ืขื‘ื•ืจ ืžืื•ืช ืฉืจืชื™ื. ื›ืืŸ ะธ ื›ืืŸ.

ืžืชื›ื•ื ื™ื ืœืฉืื™ืœืชื•ืช SQL ื—ื•ืœื•ืช
ืžืชื›ื•ื ื™ื ืœืฉืื™ืœืชื•ืช SQL ื—ื•ืœื•ืช

ืžืงื•ืจ: www.habr.com

ื”ื•ืกืคืช ืชื’ื•ื‘ื”