Π’Ρ‹Ρ‡ΠΈΡ‰Π°Π΅ΠΌ ΠΊΠ»ΠΎΠ½-записи ΠΈΠ· Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ Π±Π΅Π· PK

Π‘Π»ΡƒΡ‡Π°ΡŽΡ‚ΡΡ ситуации, ΠΊΠΎΠ³Π΄Π° Π² Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Π±Π΅Π· ΠΏΠ΅Ρ€Π²ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ»ΡŽΡ‡Π° ΠΈΠ»ΠΈ ΠΊΠ°ΠΊΠΎΠ³ΠΎ-Ρ‚ΠΎ Π΄Ρ€ΡƒΠ³ΠΎΠ³ΠΎ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΠ³ΠΎ индСкса ΠΏΠΎ нСдосмотру ΠΏΠΎΠΏΠ°Π΄Π°ΡŽΡ‚ ΠΏΠΎΠ»Π½Ρ‹Π΅ ΠΊΠ»ΠΎΠ½Ρ‹ ΡƒΠΆΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… записСй.

Π’Ρ‹Ρ‡ΠΈΡ‰Π°Π΅ΠΌ ΠΊΠ»ΠΎΠ½-записи ΠΈΠ· Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ Π±Π΅Π· PK

НапримСр, ΠΏΠΈΡˆΡƒΡ‚ΡΡ Π² PostgreSQL COPY-ΠΏΠΎΡ‚ΠΎΠΊΠΎΠΌ значСния хронологичСской ΠΌΠ΅Ρ‚Ρ€ΠΈΠΊΠΈ, Π° ΠΏΠΎΡ‚ΠΎΠΌ Π²Π½Π΅Π·Π°ΠΏΠ½Ρ‹ΠΉ сбой, ΠΈ Ρ‡Π°ΡΡ‚ΡŒ ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ‡Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΡ‚ ΠΏΠΎΠ²Ρ‚ΠΎΡ€Π½ΠΎ.

Как ΠΈΠ·Π±Π°Π²ΠΈΡ‚ΡŒ Π±Π°Π·Ρƒ ΠΎΡ‚ Π½Π΅Π½ΡƒΠΆΠ½Ρ‹Ρ… ΠΊΠ»ΠΎΠ½ΠΎΠ²?

Когда PK Π½Π΅ ΠΏΠΎΠΌΠΎΡ‰Π½ΠΈΠΊ

Π‘Π°ΠΌΡ‹ΠΉ простой способ β€” Π²ΠΎΠΎΠ±Ρ‰Π΅ Π½Π΅ Π΄ΠΎΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ возникновСния Ρ‚Π°ΠΊΠΎΠΉ ситуации. НапримСр, Π½Π°ΠΊΠ°Ρ‚ΠΈΡ‚ΡŒ-Ρ‚Π°ΠΊΠΈ PRIMARY KEY. Но это Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Π½Π΅ всСгда Π±Π΅Π· увСличСния объСма Ρ…Ρ€Π°Π½ΠΈΠΌΡ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ….

НапримСр, Ссли Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒ исходной систСмы Π²Ρ‹ΡˆΠ΅, Ρ‡Π΅ΠΌ Ρ‚ΠΎΡ‡Π½ΠΎΡΡ‚ΡŒ поля Π² Π‘Π”:

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 записался Π² Π±Π°Π·Ρƒ с ts Π½Π° сСкунду Ρ€Π°Π½ΡŒΡˆΠ΅, Π½ΠΎ остался Π²ΠΏΠΎΠ»Π½Π΅ Π²Π°Π»ΠΈΠ΄Π½Ρ‹ΠΌ с ΠΏΡ€ΠΈΠΊΠ»Π°Π΄Π½ΠΎΠΉ Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния (вСдь значСния data β€” Ρ€Π°Π·Π½Ρ‹Π΅!).

ΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ, ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ PK(metric, ts) β€” Π½ΠΎ Ρ‚ΠΎΠ³Π΄Π° ΠΌΡ‹ Π±ΡƒΠ΄Π΅ΠΌ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ ΠΊΠΎΠ½Ρ„Π»ΠΈΠΊΡ‚Ρ‹ вставки для Π²Π°Π»ΠΈΠ΄Π½Ρ‹Ρ… Π΄Π°Π½Π½Ρ‹Ρ….

МоТно ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ PK(metric, ts, data) β€” Π½ΠΎ это сильно ΡƒΠ²Π΅Π»ΠΈΡ‡ΠΈΡ‚ Π΅Π³ΠΎ объСм, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ ΠΌΡ‹ ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ-Ρ‚ΠΎ Π½Π΅ Π±ΡƒΠ΄Π΅ΠΌ.

ΠŸΠΎΡΡ‚ΠΎΠΌΡƒ самый ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ β€” ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΉ Π½Π΅ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ индСкс (metric, 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 записСй Π² Ρ€Π°Π·Ρ€Π΅Π·Π΅ ΠΏΠΎΠ»Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π° строки Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹. Π‘Π°ΠΌΡ‹ΠΉ просто Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ β€” ΡΠΊΠ°ΡΡ‚ΠΎΠ²Π°Ρ‚ΡŒ всю строку Π² text:

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]

Π”Π°, всС ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ: наши 3 записи ΠΎΡ‚ΠΎΠ±Ρ€Π°Π»ΠΈΡΡŒ Π·Π° СдинствСнный Seq Scan всСй Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹, Π° Delete-ΡƒΠ·Π΅Π» использовал для поиска Π΄Π°Π½Π½Ρ‹Ρ… ΠΎΠ΄Π½ΠΎΠΊΡ€Π°Ρ‚Π½Ρ‹ΠΉ ΠΏΡ€ΠΎΡ…ΠΎΠ΄ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Tid Scan:

->  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, ΠΈ ΠΎΠ΄Π½ΠΎ β€” Π² Delete.

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