PK ์—†์ด ํ…Œ์ด๋ธ”์—์„œ ํด๋ก  ๋ ˆ์ฝ”๋“œ ์ง€์šฐ๊ธฐ

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ƒํ™ฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ํ‚ค๊ฐ€ ์—†๋Š” ํ…Œ์ด๋ธ”์— ๋˜๋Š” ์ผ๋ถ€ ๋‹ค๋ฅธ ๊ณ ์œ  ์ธ๋ฑ์Šค๋Š” ๊ฐ๋…์œผ๋กœ ์ธํ•ด ๊ธฐ์กด ๋ ˆ์ฝ”๋“œ์˜ ์™„์ „ํ•œ ๋ณต์ œ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

PK ์—†์ด ํ…Œ์ด๋ธ”์—์„œ ํด๋ก  ๋ ˆ์ฝ”๋“œ ์ง€์šฐ๊ธฐ

์˜ˆ๋ฅผ ๋“ค์–ด COPY ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฐ๋Œ€์ˆœ ๋ฉ”ํŠธ๋ฆญ ๊ฐ’์ด PostgreSQL์— ๊ธฐ๋ก๋œ ํ›„ ๊ฐ‘์ž‘์Šค๋Ÿฌ์šด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์™„์ „ํžˆ ๋™์ผํ•œ ๋ฐ์ดํ„ฐ ์ค‘ ์ผ๋ถ€๊ฐ€ ๋‹ค์‹œ ๋„์ฐฉํ•ฉ๋‹ˆ๋‹ค.

๋ถˆํ•„์š”ํ•œ ํด๋ก ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?

PK๊ฐ€ ๋„์šฐ๋ฏธ๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ

๊ฐ€์žฅ ์‰ฌ์šด ๋ฐฉ๋ฒ•์€ ์• ์ดˆ์— ๊ทธ๋Ÿฌํ•œ ์ƒํ™ฉ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๋ฐฉ์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๊ธฐ๋ณธ ํ‚ค๋ฅผ ๊ตด๋ฆฝ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์˜ ์–‘์„ ๋Š˜๋ฆฌ์ง€ ์•Š๊ณ ๋Š” ์ด๊ฒƒ์ด ํ•ญ์ƒ ๊ฐ€๋Šฅํ•œ ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์†Œ์Šค ์‹œ์Šคํ…œ์˜ ์ •ํ™•๋„๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ•„๋“œ์˜ ์ •ํ™•๋„๋ณด๋‹ค ๋†’์€ ๊ฒฝ์šฐ:

metric   | ts                  | data
--------------------------------------------------
cpu.busy | 2019-12-20 00:00:00 | {"value" : 12.34}
cpu.busy | 2019-12-20 00:00:01 | {"value" : 10}
cpu.busy | 2019-12-20 00:00:01 | {"value" : 11.2}
cpu.busy | 2019-12-20 00:00:03 | {"value" : 15.7}

๋ˆˆ์น˜์ฑ„์…จ๋‚˜์š”? 00:00:02 ๋Œ€์‹  ์นด์šดํŠธ๋‹ค์šด์€ XNUMX์ดˆ ์ „์— ts๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ธฐ๋ก๋˜์—ˆ์ง€๋งŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ด€์ ์—์„œ๋Š” ๊ฝค ์œ ํšจํ•œ ์ƒํƒœ๋กœ ์œ ์ง€๋˜์—ˆ์Šต๋‹ˆ๋‹ค(๊ฒฐ๊ตญ ๋ฐ์ดํ„ฐ ๊ฐ’์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค!).

๋ฌผ๋ก  ๋‹น์‹ ์€ ๊ทธ๊ฒƒ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค PK(๋ฏธํ„ฐ๋ฒ•, TS) - ํ•˜์ง€๋งŒ ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์‚ฝ์ž… ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

ํ•  ์ˆ˜์žˆ๋‹ค PK(๋ฏธํ„ฐ๋ฒ•, TS, ๋ฐ์ดํ„ฐ) -๊ทธ๋Ÿฌ๋‚˜ ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ณผ๋ฅจ์ด ํฌ๊ฒŒ ์ฆ๊ฐ€ํ•˜๋ฏ€๋กœ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ฐ€์žฅ ์˜ฌ๋ฐ”๋ฅธ ์˜ต์…˜์€ ์ผ๋ฐ˜ ๊ณ ์œ ํ•˜์ง€ ์•Š์€ ์ธ๋ฑ์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. (๋ฏธํ„ฐ๋ฒ•, TS) ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด ์‚ฌํ›„์— ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

"ํด๋ก  ์ „์Ÿ์ด ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค"

์–ด๋–ค ์‚ฌ๊ณ ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์ด์ œ ํ…Œ์ด๋ธ”์—์„œ ๋ณต์ œ ๊ธฐ๋ก์„ ์‚ญ์ œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

PK ์—†์ด ํ…Œ์ด๋ธ”์—์„œ ํด๋ก  ๋ ˆ์ฝ”๋“œ ์ง€์šฐ๊ธฐ

์›๋ณธ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋ธ๋งํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

CREATE TABLE tbl(k text, v integer);

INSERT INTO tbl
VALUES
  ('a', 1)
, ('a', 3)
, ('b', 2)
, ('b', 2) -- oops!
, ('c', 3)
, ('c', 3) -- oops!!
, ('c', 3) -- oops!!
, ('d', 4)
, ('e', 5)
;

์—ฌ๊ธฐ์„œ ์†์ด ์„ธ๋ฒˆ ๋–จ๋ฆฌ๊ณ  Ctrl+V๊ฐ€ ๋ฉˆ์ท„๋Š”๋ฐ ์ง€๊ธˆ์€...

๋จผ์ € ํ…Œ์ด๋ธ”์ด ๋งค์šฐ ํด ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ชจ๋“  ํด๋ก ์„ ์ฐพ์€ ํ›„์— ๋ฌธ์ž ๊ทธ๋Œ€๋กœ "์†๊ฐ€๋ฝ์„ ์ฐ”๋Ÿฌ" ์‚ญ์ œํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ํŠน์ • ๊ธฐ๋ก์„ ์žฌ๊ฒ€์ƒ‰ํ•˜์ง€ ์•Š๊ณ .

๊ทธ๋ฆฌ๊ณ  ๊ทธ๋Ÿฐ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค - ์ด๊ฒƒ์€ ctid๋กœ ์ฃผ์†Œ ์ง€์ •, ํŠน์ • ๋ ˆ์ฝ”๋“œ์˜ ๋ฌผ๋ฆฌ์  ์‹๋ณ„์ž์ž…๋‹ˆ๋‹ค.

์ฆ‰, ์šฐ์„  ํ…Œ์ด๋ธ” ํ–‰์˜ ์ „์ฒด ๋‚ด์šฉ์— ๋Œ€ํ•œ ๋งฅ๋ฝ์—์„œ ๋ ˆ์ฝ”๋“œ์˜ ctid๋ฅผ ์ˆ˜์ง‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ์˜ต์…˜์€ ์ „์ฒด ์ค„์„ ํ…์ŠคํŠธ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

SELECT
  T::text
, array_agg(ctid) ctids
FROM
  tbl T
GROUP BY
  1;

t     | ctids
---------------------------------
(e,5) | {"(0,9)"}
(d,4) | {"(0,8)"}
(c,3) | {"(0,5)","(0,6)","(0,7)"}
(b,2) | {"(0,3)","(0,4)"}
(a,3) | {"(0,2)"}
(a,1) | {"(0,1)"}

์•ˆ ์บ์ŠคํŒ…์ด ๊ฐ€๋Šฅํ•œ๊ฐ€์š”?์›์น™์ ์œผ๋กœ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ์ด ํ…Œ์ด๋ธ”์˜ ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ•  ๋•Œ๊นŒ์ง€ ํ•ญ๋“ฑ ์—ฐ์‚ฐ์ž๊ฐ€ ์—†๋Š” ์œ ํ˜•:

CREATE TABLE tbl(k text, v integer, x point);
SELECT
  array_agg(ctid) ctids
FROM
  tbl T
GROUP BY
  T;
-- ERROR:  could not identify an equality operator for type tbl

์˜ˆ, ๋ฐฐ์—ด์— ํ•ญ๋ชฉ์ด ๋‘ ๊ฐœ ์ด์ƒ ์žˆ์œผ๋ฉด ๋ชจ๋‘ ํด๋ก ์ด๋ผ๋Š” ๊ฒƒ์„ ์ฆ‰์‹œ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ƒฅ ๋‘์ž:

SELECT
  unnest(ctids[2:])
FROM
  (
    SELECT
      array_agg(ctid) ctids
    FROM
      tbl T
    GROUP BY
      T::text
  ) T;

unnest
------
(0,6)
(0,7)
(0,4)

์งง๊ฒŒ ์“ฐ๊ณ  ์‹ถ์€ ๋ถ„๋“ค์€๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

SELECT
  unnest((array_agg(ctid))[2:])
FROM
  tbl T
GROUP BY
  T::text;

์ง๋ ฌํ™”๋œ ๋ฌธ์ž์—ด์˜ ๊ฐ’ ์ž์ฒด๋Š” ์šฐ๋ฆฌ์—๊ฒŒ ํฅ๋ฏธ๋กญ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ•˜์œ„ ์ฟผ๋ฆฌ์˜ ๋ฐ˜ํ™˜๋œ ์—ด์—์„œ ํ•ด๋‹น ๊ฐ’์„ ์‚ญ์ œํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด์•ผ ํ•  ์ผ์ด ์กฐ๊ธˆ ๋‚จ์•˜์Šต๋‹ˆ๋‹ค. DELETE๊ฐ€ ์šฐ๋ฆฌ๊ฐ€ ๋ฐ›์€ ์„ธํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜์„ธ์š”.

DELETE FROM
  tbl
WHERE
  ctid = ANY(ARRAY(
    SELECT
      unnest(ctids[2:])
    FROM
      (
        SELECT
          array_agg(ctid) ctids
        FROM
          tbl T
        GROUP BY
          T::text
      ) T
  )::tid[]);

์Šค์Šค๋กœ ํ™•์ธํ•ด ๋ด…์‹œ๋‹ค:

PK ์—†์ด ํ…Œ์ด๋ธ”์—์„œ ํด๋ก  ๋ ˆ์ฝ”๋“œ ์ง€์šฐ๊ธฐ
[explain.tensor.ru ์ฐธ์กฐ]

์˜ˆ, ๋ชจ๋“  ๊ฒƒ์ด ์ •ํ™•ํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด ํ…Œ์ด๋ธ”์˜ ์œ ์ผํ•œ Seq ์Šค์บ”์„ ์œ„ํ•ด 3๊ฐœ์˜ ๋ ˆ์ฝ”๋“œ๊ฐ€ ์„ ํƒ๋˜์—ˆ์œผ๋ฉฐ ๋ฐ์ดํ„ฐ ๊ฒ€์ƒ‰์—๋Š” ์‚ญ์ œ ๋…ธ๋“œ๊ฐ€ ์‚ฌ์šฉ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. Tid ์Šค์บ”์„ ์‚ฌ์šฉํ•œ ๋‹จ์ผ ํŒจ์Šค:

->  Tid Scan on tbl (actual time=0.050..0.051 rows=3 loops=1)
      TID Cond: (ctid = ANY ($0))

๋งŽ์€ ๊ธฐ๋ก์„ ์ง€์› ๋‹ค๋ฉด, VACUUM ANALYZE๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์‹ญ์‹œ์˜ค.

๋” ํฐ ํ…Œ์ด๋ธ”๊ณผ ๋” ๋งŽ์€ ์ค‘๋ณต ํ•ญ๋ชฉ์ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

TRUNCATE TABLE tbl;

INSERT INTO tbl
SELECT
  chr(ascii('a'::text) + (random() * 26)::integer) k -- a..z
, (random() * 100)::integer v -- 0..99
FROM
  generate_series(1, 10000) i;

PK ์—†์ด ํ…Œ์ด๋ธ”์—์„œ ํด๋ก  ๋ ˆ์ฝ”๋“œ ์ง€์šฐ๊ธฐ
[explain.tensor.ru ์ฐธ์กฐ]

๋”ฐ๋ผ์„œ ์ด ๋ฐฉ๋ฒ•์€ ์„ฑ๊ณต์ ์œผ๋กœ ์ž‘๋™ํ•˜์ง€๋งŒ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์‚ญ์ œ๋˜๋Š” ๋ชจ๋“  ๊ธฐ๋ก์— ๋Œ€ํ•ด Tid Scan์—์„œ ์ฝ์€ ๋ฐ์ดํ„ฐ ํŽ˜์ด์ง€๊ฐ€ ํ•˜๋‚˜, ์‚ญ์ œ์—์„œ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ ํŽ˜์ด์ง€๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : habr.com

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