Π‘Π»ΡΡΠ°ΡΡΡΡ ΡΠΈΡΡΠ°ΡΠΈΠΈ, ΠΊΠΎΠ³Π΄Π° Π² ΡΠ°Π±Π»ΠΈΡΡ Π±Π΅Π· ΠΏΠ΅ΡΠ²ΠΈΡΠ½ΠΎΠ³ΠΎ ΠΊΠ»ΡΡΠ° ΠΈΠ»ΠΈ ΠΊΠ°ΠΊΠΎΠ³ΠΎ-ΡΠΎ Π΄ΡΡΠ³ΠΎΠ³ΠΎ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΠΎΠ³ΠΎ ΠΈΠ½Π΄Π΅ΠΊΡΠ° ΠΏΠΎ Π½Π΅Π΄ΠΎΡΠΌΠΎΡΡΡ ΠΏΠΎΠΏΠ°Π΄Π°ΡΡ ΠΏΠΎΠ»Π½ΡΠ΅ ΠΊΠ»ΠΎΠ½Ρ ΡΠΆΠ΅ ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠΈΡ Π·Π°ΠΏΠΈΡΠ΅ΠΉ.
ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, ΠΏΠΈΡΡΡΡΡ Π² 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) ΠΈ ΡΠ°Π·Π±ΠΈΡΠ°ΡΡΡΡ Ρ ΠΏΡΠΎΠ±Π»Π΅ΠΌΠ°ΠΌΠΈ ΠΏΠΎΡΡΡΠ°ΠΊΡΡΠΌ, Π΅ΡΠ»ΠΈ ΠΎΠ½ΠΈ Π²ΡΠ΅-ΡΠ°ΠΊΠΈ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡΡ.
Β«ΠΠΎΠΉΠ½Π° ΠΊΠ»ΠΎΠ½ΠΈΡΠ΅ΡΠΊΠ°Ρ Π½Π°ΡΠ°Π»Π°ΡΡΒ»
Π‘Π»ΡΡΠΈΠ»Π°ΡΡ ΠΊΠ°ΠΊΠ°Ρ-ΡΠΎ Π°Π²Π°ΡΠΈΡ, ΠΈ ΡΠ΅ΠΏΠ΅ΡΡ Π½Π°ΠΌ ΠΏΡΠ΅Π΄ΡΡΠΎΠΈΡ ΡΠ½ΠΈΡΡΠΎΠΆΠΈΡΡ ΠΊΠ»ΠΎΠ½-Π·Π°ΠΏΠΈΡΠΈ ΠΈΠ· ΡΠ°Π±Π»ΠΈΡΡ.
ΠΠ°Π²Π°ΠΉΡΠ΅ ΡΠΌΠΎΠ΄Π΅Π»ΠΈΡΡΠ΅ΠΌ ΠΈΡΡ
ΠΎΠ΄Π½ΡΠ΅ Π΄Π°Π½Π½ΡΠ΅:
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 Π·Π°ΠΏΠΈΡΠ΅ΠΉ Π² ΡΠ°Π·ΡΠ΅Π·Π΅ ΠΏΠΎΠ»Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ½ΡΠ΅Π½ΡΠ° ΡΡΡΠΎΠΊΠΈ ΡΠ°Π±Π»ΠΈΡΡ. Π‘Π°ΠΌΡΠΉ ΠΏΡΠΎΡΡΠΎ Π²Π°ΡΠΈΠ°Π½Ρ β ΡΠΊΠ°ΡΡΠΎΠ²Π°ΡΡ Π²ΡΡ ΡΡΡΠΎΠΊΡ Π² 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[]);
ΠΡΠΎΠ²Π΅ΡΠΈΠΌ ΡΠ΅Π±Ρ:
ΠΠ°, Π²ΡΠ΅ ΠΏΡΠ°Π²ΠΈΠ»ΡΠ½ΠΎ: Π½Π°ΡΠΈ 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))
ΠΡΠ»ΠΈ Π·Π°ΡΠΈΡΡΠΈΠ»ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ Π·Π°ΠΏΠΈΡΠ΅ΠΉ,
ΠΡΠΎΠ²Π΅ΡΠΈΠΌ Π΄Π»Ρ ΡΠ°Π±Π»ΠΈΡΡ ΠΏΠΎΠ±ΠΎΠ»ΡΡΠ΅ ΠΈ Ρ Π±ΠΎΠ»ΡΡΠΈΠΌ ΠΊΠΎΠ»ΠΈΡΠ΅ΡΡΠ²ΠΎΠΌ Π΄ΡΠ±Π»Π΅ΠΉ:
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;
ΠΡΠ°ΠΊ, ΡΠΏΠΎΡΠΎΠ± ΡΡΠΏΠ΅ΡΠ½ΠΎ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ, Π½ΠΎ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡ Π½Π°Π΄ΠΎ Ρ ΠΈΠ·Π²Π΅ΡΡΠ½ΠΎΠΉ ΠΎΡΡΠΎΡΠΎΠΆΠ½ΠΎΡΡΡΡ. ΠΠΎΡΠΎΠΌΡ ΡΡΠΎ Π½Π° ΠΊΠ°ΠΆΠ΄ΡΡ ΡΠ΄Π°Π»ΡΠ΅ΠΌΡΡ Π·Π°ΠΏΠΈΡΡ ΠΏΡΠΈΡ
ΠΎΠ΄ΠΈΡΡΡ ΠΎΠ΄Π½ΠΎ ΡΡΠ΅Π½ΠΈΠ΅ ΡΡΡΠ°Π½ΠΈΡΡ Π΄Π°Π½Π½ΡΡ
Π² Tid Scan, ΠΈ ΠΎΠ΄Π½ΠΎ β Π² Delete.
ΠΡΡΠΎΡΠ½ΠΈΠΊ: habr.com