์ž˜๋ชป๋œ SQL ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ ˆ์‹œํ”ผ

๋ช‡๋‹ฌ ์ „ ์šฐ๋ฆฌ๋Š” ๋ฐœํ‘œํ–ˆ๋‹ค explain.tensor.ru - ๊ณต๊ณต์˜ ์ฟผ๋ฆฌ ๊ณ„ํš ๊ตฌ๋ฌธ ๋ถ„์„ ๋ฐ ์‹œ๊ฐํ™” ์„œ๋น„์Šค PostgreSQL์—.

๊ทธ ์ดํ›„๋กœ ์ด๋ฏธ 6000ํšŒ ์ด์ƒ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ๊ฐ„๊ณผํ•  ์ˆ˜ ์žˆ๋Š” ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ตฌ์กฐ์  ๋‹จ์„œ, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ž˜๋ชป๋œ SQL ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ ˆ์‹œํ”ผ

๊ทธ๋“ค์—๊ฒŒ ๊ท€๋ฅผ ๊ธฐ์šธ์ด๋ฉด ๊ท€ํ•˜์˜ ์š”์ฒญ์ด "๋งค๋„๋Ÿฌ์›Œ์งˆ ๊ฒƒ"์ž…๋‹ˆ๋‹ค. ๐Ÿ™‚

๊ทธ๋Ÿฌ๋‚˜ ์ง„์ง€ํ•˜๊ฒŒ ์š”์ฒญ์„ ๋Š๋ฆฌ๊ณ  ๋ฆฌ์†Œ์Šค ์ธก๋ฉด์—์„œ "๊ฒŒ๊ฑธ์Šค๋Ÿฝ๊ฒŒ" ๋งŒ๋“œ๋Š” ๋งŽ์€ ์ƒํ™ฉ, ์ผ๋ฐ˜์ ์ด๋ฉฐ ๊ณ„ํš์˜ ๊ตฌ์กฐ์™€ ๋ฐ์ดํ„ฐ๋กœ ์ธ์‹ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค..

์ด ๊ฒฝ์šฐ ๊ฐœ๋ณ„ ๊ฐœ๋ฐœ์ž๋Š” ์ž์‹ ์˜ ๊ฒฝํ—˜์—๋งŒ ์˜์กดํ•˜์—ฌ ์Šค์Šค๋กœ ์ตœ์ ํ™” ์˜ต์…˜์„ ์ฐพ์„ ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๋ฌด์Šจ ์ผ์ด ์ผ์–ด๋‚˜๊ณ  ์žˆ๋Š”์ง€, ์ด์œ ๊ฐ€ ๋ฌด์—‡์ธ์ง€, ํ•ด๊ฒฐ์ฑ…์„ ๋‚ด๋†“๋Š” ๋ฐฉ๋ฒ•. ์šฐ๋ฆฌ๊ฐ€ ํ•œ ์ผ์ž…๋‹ˆ๋‹ค.

์ž˜๋ชป๋œ SQL ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ ˆ์‹œํ”ผ

์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ๊ฐ€ ์–ด๋–ป๊ฒŒ ์ •์˜๋˜๊ณ  ์–ด๋–ค ๊ถŒ์žฅ ์‚ฌํ•ญ์œผ๋กœ ์ด์–ด์ง€๋Š”์ง€ ์ž์„ธํžˆ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฃผ์ œ์— ๋” ์ž˜ ๋ชฐ์ž…ํ•˜๋ ค๋ฉด ๋จผ์ € ๋‹ค์Œ์—์„œ ํ•ด๋‹น ๋ธ”๋ก์„ ๋“ค์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. PGConf.Russia 2020์—์„œ์˜ ๋ณด๊ณ ์„œ, ๊ทธ๋Ÿฐ ๋‹ค์Œ ๊ฐ ์˜ˆ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋ถ„์„์œผ๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

#1: ์ธ๋ฑ์Šค "undersorting"

์–ธ์ œ

ํด๋ผ์ด์–ธํŠธ "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)

์–ธ์ œ

"NJSC Lyutik"์„ ๋Œ€์‹ ํ•˜์—ฌ ์ฒด๊ฒฐ๋œ ํด๋ผ์ด์–ธํŠธ "LLC Kolokolchik"์— ๋Œ€ํ•œ ๋ชจ๋“  ๊ณ„์•ฝ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์‹๋ณ„ ๋ฐฉ๋ฒ•

-> 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 ์ฐธ์กฐ]

์—ฌ๊ธฐ์„œ ๋น„ํŠธ๋งต ํž™ ์Šค์บ”์€ ๊ทธ ์ž์ฒด๋กœ ๋งค์šฐ ํšจ๊ณผ์ ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด๋“์ด ๋” ์ž‘์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์–ด์จŒ๋“  7๋ฐฐ ๋” ๋น ๋ฅด๊ณ  2.5๋ฐฐ ๋” ์ ์€ ์ฝ๊ธฐ.

#3: ์ธ๋ฑ์Šค ๊ฒฐํ•ฉ(BitmapOr)

์–ธ์ œ

๊ฐ€์žฅ ์˜ค๋ž˜๋œ "์†Œ์œ " ๋˜๋Š” ํ• ๋‹น๋˜์ง€ ์•Š์€ ์ฒ˜๋ฆฌ ์š”์ฒญ 20๊ฐœ๋ฅผ ์šฐ์„  ์ˆœ์œ„๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์‹๋ณ„ ๋ฐฉ๋ฒ•

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

์ถ”์ฒœ

์‚ฌ์šฉ ์œ ๋‹ˆ์˜จ [์ „์ฒด] ๊ฐ ์กฐ๊ฑด 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 ์•ˆํ‹ฐํŒจํ„ด: ์œ ํ•ดํ•œ JOIN ๋ฐ OR ะธ PostgreSQL ์•ˆํ‹ฐํŒจํ„ด: ์ด๋ฆ„๋ณ„ ๊ฒ€์ƒ‰์˜ ๋ฐ˜๋ณต์  ๊ฐœ์„  ๋˜๋Š” "์•ž๋’ค๋กœ ์ตœ์ ํ™”"์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ.

์ผ๋ฐ˜ํ™” ๋ฒ„์ „ ์—ฌ๋Ÿฌ ํ‚ค๋กœ ์ •๋ ฌ๋œ ์„ ํƒ (const / NULL ์Œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ) ๊ธฐ์‚ฌ์—์„œ ๋…ผ์˜๋ฉ๋‹ˆ๋‹ค. SQL HowTo: ์ฟผ๋ฆฌ์—์„œ ์ง์ ‘ while ๋ฃจํ”„ ์ž‘์„ฑ ๋˜๋Š” "๊ธฐ๋ณธ XNUMX๋ฐฉํ–ฅ".

#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 ์•ˆํ‹ฐํŒจํ„ด: "์ฃฝ์€" ๋ฌด๋ฆฌ์™€ ์‹ธ์šฐ๊ธฐ.

๊ทธ๋Ÿฌ๋‚˜ 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 ์•ˆํ‹ฐํŒจํ„ด: ์‚ฌ์ „ ํžˆํŠธ ํ—ค๋น„ ์กฐ์ธ.

#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 ์ฐธ์กฐ]

๋ถ„๋ช…ํ•œ ์ด์œ ๋กœ ๋””์Šคํฌ๊ฐ€ ์•„๋‹Œ ๋ฉ”๋ชจ๋ฆฌ๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ์ฟผ๋ฆฌ๊ฐ€ ํ›จ์”ฌ ๋นจ๋ผ์ง‘๋‹ˆ๋‹ค. ๋™์‹œ์— ๋ถ€ํ•˜์˜ ์ผ๋ถ€๋„ HDD์—์„œ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ๋งŽ์€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ• ๋‹นํ•˜๋Š” ๊ฒƒ์ด ํ•ญ์ƒ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์ดํ•ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ๋ชจ๋“  ์‚ฌ๋žŒ์—๊ฒŒ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

#9: ๊ด€๋ จ ์—†๋Š” ํ†ต๊ณ„

์–ธ์ œ

ํ•œ ๋ฒˆ์— ๊ธฐ์ง€์— ๋งŽ์€ ์–‘์„ ์Ÿ์•„ ๋ถ€์—ˆ์ง€๋งŒ ๋ชฐ์•„ ๋‚ผ ์‹œ๊ฐ„์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค ANALYZE.

์‹๋ณ„ ๋ฐฉ๋ฒ•

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

์ถ”์ฒœ

๊ฐ™์€ ์ง€์ถœ ANALYZE.

์ด ์ƒํ™ฉ์€ ์— ์ž์„ธํžˆ ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. PostgreSQL ์•ˆํ‹ฐํŒจํ„ด: ํ†ต๊ณ„๋Š” ๋ชจ๋“  ๊ฒƒ์˜ ํ•ต์‹ฌ์ž…๋‹ˆ๋‹ค..

#10: "๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค."

์–ธ์ œ

๊ฒฝ์Ÿ ์š”์ฒญ์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ์ž ๊ธˆ์ด ์žˆ๊ฑฐ๋‚˜ CPU/ํ•˜์ดํผ๋ฐ”์ด์ € ํ•˜๋“œ์›จ์–ด ๋ฆฌ์†Œ์Šค๊ฐ€ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์‹๋ณ„ ๋ฐฉ๋ฒ•

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

์ถ”์ฒœ

์™ธ๋ถ€ ์‚ฌ์šฉ ๊ฐ์‹œ ์ฒด๊ณ„ ์ฐจ๋‹จ ๋˜๋Š” ๋น„์ •์ƒ์ ์ธ ๋ฆฌ์†Œ์Šค ์†Œ๋น„๋ฅผ ์œ„ํ•œ ์„œ๋ฒ„. ์ˆ˜๋ฐฑ ๋Œ€์˜ ์„œ๋ฒ„์— ๋Œ€ํ•ด ์ด ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฒ„์ „์— ๋Œ€ํ•ด ์ด๋ฏธ ์ด์•ผ๊ธฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ะธ ์—ฌ๊ธฐ์—.

์ž˜๋ชป๋œ SQL ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ ˆ์‹œํ”ผ
์ž˜๋ชป๋œ SQL ์ฟผ๋ฆฌ์— ๋Œ€ํ•œ ๋ ˆ์‹œํ”ผ

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€