PK жок таблицадан клондук жазууларды тазалоо

Качан жагдайлар болот негизги ачкычы жок столго же кандайдыр бир башка уникалдуу индекс, көзөмөлгө алынгандыктан, буга чейин болгон жазуулардын толук клондору камтылган.

PK жок таблицадан клондук жазууларды тазалоо

Мисалы, хронологиялык метриканын маанилери PostgreSQLге COPY агымы аркылуу жазылат, андан кийин күтүлбөгөн жерден ката болуп, толугу менен окшош маалыматтардын бир бөлүгү кайра келет.

Керексиз клондордун маалымат базасын кантип тазалоо керек?

ПК жардамчы болбогондо

Эң оңой жолу - биринчи кезекте мындай кырдаалдын алдын алуу. Мисалы, БАШКЫ АЧКЫЧКЫ айлантыңыз. Бирок бул сакталган маалыматтардын көлөмүн көбөйтүүсүз дайыма эле мүмкүн боло бербейт.

Мисалы, булак системасынын тактыгы маалымат базасындагы талаанын тактыгынан жогору болсо:

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 менен бир секунд мурун жазылган, бирок колдонмонун көз карашынан алганда бир топ жарактуу болгон (анткени, маалымат баалуулуктары ар кандай!).

Албетте, сен кыла аласың 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 жок таблицадан клондук жазууларды тазалоо
[express.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 жок таблицадан клондук жазууларды тазалоо
[express.tensor.ru сайтынан көрүү]

Ошентип, ыкма ийгиликтүү иштейт, бирок аны бир аз этияттык менен колдонуу керек. Себеби, жок кылынган ар бир жазуу үчүн Tid Scan'да окулуучу бир маалымат барагы жана Жок кылууда бирөө бар.

Source: www.habr.com

Комментарий кошуу