Clirio cofnodion clôn o fwrdd heb PK

Mae yna sefyllfaoedd pan i mewn i dabl heb allwedd sylfaenol neu mae rhyw fynegai unigryw arall, oherwydd amryfusedd, yn cynnwys cloniau cyflawn o gofnodion sy'n bodoli eisoes.

Clirio cofnodion clôn o fwrdd heb PK

Er enghraifft, mae gwerthoedd metrig cronolegol yn cael eu hysgrifennu i PostgreSQL gan ddefnyddio ffrwd COPY, ac yna mae methiant sydyn, ac mae rhywfaint o ddata hollol union yr un fath yn dod i mewn eto.

Sut i gael gwared ar gloniau diangen o gronfa ddata?

Pan nad yw PK yn ddefnyddiol

Yr ateb symlaf yw atal y sefyllfa hon rhag digwydd yn y lle cyntaf. Er enghraifft, trwy weithredu ALLWEDD GYNRAIF. Ond nid yw hyn bob amser yn bosibl heb gynyddu faint o ddata sy'n cael ei storio.

Er enghraifft, os yw cywirdeb y system ffynhonnell yn uwch na chywirdeb y maes yn y gronfa ddata:

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}

Wedi sylwi? Cafodd y cyfrif i lawr, yn lle 00:00:02, ei gofnodi yn gronfa ddata ts eiliad ynghynt, ond roedd yn parhau i fod yn berffaith ddilys o safbwynt ymarferol (wedi'r cyfan, mae gwerthoedd y data yn wahanol!).

Wrth gwrs, gellir ei wneud PK(metrig, ts) — ond yna byddwn yn cael gwrthdaro mewnosod ar gyfer data dilys.

Gallu gwneud PK(metrig, ts, data) - ond bydd hyn yn cynyddu ei gyfaint yn fawr, na fyddwn yn ei ddefnyddio.

Felly, yr opsiwn mwyaf cywir yw creu mynegai rheolaidd nad yw'n unigryw. (metrig, ts) a delio â phroblemau ar ôl y ffaith os byddant yn codi.

Mae Rhyfeloedd y Cloniau wedi dechrau.

Digwyddodd rhyw fath o ddamwain, ac yn awr mae'n rhaid i ni ddinistrio'r cofnodion clôn o'r tabl.

Clirio cofnodion clôn o fwrdd heb PK

Gadewch i ni efelychu'r data cychwynnol:

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)
;

Dyma ni’n crynu’n llaw dair gwaith, aeth Ctrl+V yn sownd, a dyma fe...

Yn gyntaf, gadewch i ni ddeall y gall ein tabl fod yn eithaf mawr, felly ar ôl i ni ddod o hyd i'r holl gloniau, bydd angen i ni "brocio bys" yn llythrennol i'w dileu. cofnodion penodol heb chwilio amdanynt eto.

Ac mae yna ffordd o'r fath - dyma hi cyfeiriadu ctid, y dynodwr ffisegol ar gyfer cofnod penodol.

Felly, yn gyntaf oll, mae angen i ni gasglu CTIDau'r cofnodion yn seiliedig ar gynnwys llawn rhes y tabl. Yr opsiwn symlaf yw trosi'r rhes gyfan i destun:

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)"}

A yw'n bosibl peidio â chastio?Mewn egwyddor, mae'n bosibl yn y rhan fwyaf o achosion. Nes i chi ddechrau defnyddio meysydd yn y tabl hwn. mathau heb weithredwr cydraddoldeb:

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

Aha, rydyn ni'n gweld ar unwaith, os oes mwy nag un cofnod yn y rhes, eu bod nhw i gyd yn gloniau. Gadewch i ni gadw'r rheini yn unig:

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)

I'r rhai sy'n hoffi ysgrifennu'n fyrrachGallwch chi hefyd ei ysgrifennu fel hyn:

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

Gan nad oes gennym ddiddordeb yng ngwerth y llinyn cyfresol ei hun, fe wnaethon ni ei ollwng o golofnau a ddychwelwyd yr is-ymholiad.

Y cyfan sydd ar ôl yw gwneud i DELETE ddefnyddio'r set a dderbyniwyd gennym:

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[]);

Gadewch i ni wirio ein hunain:

Clirio cofnodion clôn o fwrdd heb PK
[edrychwch ar explain.tensor.ru]

Ie, mae hynny'n gywir: dewiswyd ein 3 chofnod mewn un Sgan Dilyniant o'r tabl cyfan, a defnyddiwyd y nod Dileu i chwilio am ddata. pas sengl gyda Tid Scan:

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

Os gwnaethoch chi glirio llawer o gofnodion, Peidiwch ag anghofio rhedeg DADANSODDI GWAGOD.

Gadewch i ni wirio am dabl mwy a chyda nifer fwy o ddyblygiadau:

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;

Clirio cofnodion clôn o fwrdd heb PK
[edrychwch ar explain.tensor.ru]

Felly, mae'r dull yn gweithio'n llwyddiannus, ond dylid ei ddefnyddio'n ofalus. Oherwydd ar gyfer pob cofnod sydd wedi'i ddileu, mae un dudalen ddata wedi'i darllen yn Tid Scan ac un yn Delete.

Ffynhonnell: hab.com

Prynu gwesteio dibynadwy ar gyfer gwefannau sydd â diogelwch DDoS, gweinyddwyr VPS VDS 🔥 Prynu cynnal gwefannau dibynadwy gyda diogelwch DDoS, gweinyddion VPS VDS | ProHoster