ãããããã®ãããªäžå¿«ãªã¿ã€ã (OLTP ããŒã¿ããŒã¹ã«å¯Ÿããé·æç㪠OLAP è² è·) ããŸã ååšããŠããå Žåã¯ã©ããªãã§ãããã? ã©ããã£ãŠ ç©æ¥µçã«ããã€äº€æå°ããããã«ãã é·ãã¯ãšãªã«å²ãŸããŠçæãèžãã§ããŸããã?
çæãå±éãã
ãŸãã解決ãããåé¡ãäœã§ããããããã©ã®ããã«çºçããããå€æããŸãããã
éåžžãã®ãããªç¶æ³ãèµ·ãããŸã æ¯èŒçå°ããªããŒãã«ã®äžã«ãããããããèµ·ããå Žæ ããããã®å€åã éåžžã¯ãããéã ã¡ãŒãã«/ç·èš/å®æ ŒUPDATE ãé »ç¹ã«å®è¡ãããããŸã㯠ãããã¡ãã¥ãŒ åžžã«é²è¡äžã®ã€ãã³ãã®ã¹ããªãŒã ãåŠçãããã®ã¬ã³ãŒãã¯åžžã« INSERT/DELETE ãããŸãã
è©äŸ¡ãä»ããŠãªãã·ã§ã³ãåçŸããŠã¿ãŸãããã
CREATE TABLE tbl(k text PRIMARY KEY, v integer);
CREATE INDEX ON tbl(v DESC); -- пП ÑÑÐŸÐŒÑ ÐžÐœÐŽÐµÐºÑÑ Ð±ÑЎеЌ ÑÑÑПОÑÑ ÑейÑОМг
INSERT INTO
tbl
SELECT
chr(ascii('a'::text) + i) k
, 0 v
FROM
generate_series(0, 25) i;
ãããŠäžŠè¡ããŠãå¥ã®æ¥ç¶ã§é·ãé·ããªã¯ãšã¹ããéå§ãããè€éãªçµ±èšãåéãããŸããã ç§ãã¡ã®ããŒãã«ã«ã¯åœ±é¿ããªã:
SELECT pg_sleep(10000);
ããã§ãã«ãŠã³ã¿ãŒã® XNUMX ã€ã®å€ãäœåºŠãæŽæ°ããŸãã å®éšãçŽç²ã«ããããã«ãããããã£ãŠã¿ãŸããã
DO $$
DECLARE
i integer;
tsb timestamp;
tse timestamp;
d double precision;
BEGIN
PERFORM dblink_connect('dbname=' || current_database() || ' port=' || current_setting('port'));
FOR i IN 1..10000 LOOP
tsb = clock_timestamp();
PERFORM dblink($e$UPDATE tbl SET v = v + 1 WHERE k = 'a';$e$);
tse = clock_timestamp();
IF i % 1000 = 0 THEN
d = (extract('epoch' from tse) - extract('epoch' from tsb)) * 1000;
RAISE NOTICE 'i = %, exectime = %', lpad(i::text, 5), lpad(d::text, 5);
END IF;
END LOOP;
PERFORM dblink_disconnect();
END;
$$ LANGUAGE plpgsql;
NOTICE: i = 1000, exectime = 0.524
NOTICE: i = 2000, exectime = 0.739
NOTICE: i = 3000, exectime = 1.188
NOTICE: i = 4000, exectime = 2.508
NOTICE: i = 5000, exectime = 1.791
NOTICE: i = 6000, exectime = 2.658
NOTICE: i = 7000, exectime = 2.318
NOTICE: i = 8000, exectime = 2.572
NOTICE: i = 9000, exectime = 2.929
NOTICE: i = 10000, exectime = 3.808
ã©ãããã®ïŒ åäžã¬ã³ãŒãã®æãåçŽãª UPDATE ã§ãã£ãŠããªã å®è¡æéã7åã«äœäž â 0.524ããªç§ãã3.808ããªç§ãžïŒ ãããŠãç§ãã¡ã®è©äŸ¡ã¯ãŸããŸããã£ãããšé«ãŸã£ãŠããŸãã
ããã¯ãã¹ãŠMVCCã®ããã§ãã
ããã¯ãã¹ãŠã«ã€ããŠã§ã
VACUUM VERBOSE tbl;
INFO: vacuuming "public.tbl"
INFO: "tbl": found 0 removable, 10026 nonremovable row versions in 45 out of 45 pages
DETAIL: 10000 dead row versions cannot be removed yet, oldest xmin: 597439602
ãããæé€ãããã®ãäœããªãïŒ å¹³è¡ å®è¡äžã®ãªã¯ãšã¹ããéªéãããŠããŸã - çµå±ã®ãšããã圌ã¯ãã€ããããã®ããŒãžã§ã³ã䜿çšããããšæããããããŸãã (ãããããªã£ãã?)ããããã¯åœŒã«ãšã£ãŠå©çšå¯èœã§ããã¯ãã§ãã ãããã£ãŠãVACUUM FULL ã§ã圹ã«ç«ã¡ãŸããã
ããŒãã«ããæããããã
ãããããã®ã¯ãšãªã«ã¯ããŒãã«ãå¿ èŠãªãããšã¯ç¢ºãã§ãã ãããã£ãŠãVACUUM ãå±ãããããå°ãªããšããæåã§ãããŒãã«ããäžèŠãªãã®ããã¹ãŠåé€ããããšã§ãã·ã¹ãã ã®ããã©ãŒãã³ã¹ãé©åãªéçã«æ»ãããšè©Šã¿ãŸãã
ããæ確ã«ããããã«ããããã¡ãŒ ããŒãã«ã®äŸãèŠãŠã¿ãŸãããã ã€ãŸããINSERT/DELETE ã®å€§éã®ãããŒãçºçããããŒãã«ãå®å šã«ç©ºã«ãªãå ŽåããããŸãã ããããããã空ã§ãªãå Žåã¯ã çŸåšã®å 容ãä¿åãã.
#0: ç¶æ³ã®è©äŸ¡
åæäœã®åŸã§ãããŒãã«ã«å¯ŸããŠäœããå®è¡ã§ããããšã¯æããã§ãããããã«ã¯ããŸãæå³ããããŸãããã¡ã³ããã³ã¹ã®ãªãŒããŒããããã¿ãŒã²ãã ã¯ãšãªã®ã¹ã«ãŒããããããæããã«å€§ãããªããŸãã
åºæºãæ確ã«ããŠã¿ãŸãããã次ã®å Žåã¯ãè¡åãèµ·ããæã§ããã
- VACUUMã¯ããªãåã«çºå£²ãããŸãã
éãè² è·ããããããšãäºæ³ãããã®ã§ããã®ãŸãŸã«ããŠãããŸã 60ç§ ååã®[èªå]VACUUM以éã - ç©çããŒãã«ã®ãµã€ãºãã¿ãŒã²ãããã倧ãã
æå°ãµã€ãºã«å¯ŸããããŒãžæ° (8KB ãããã¯) ã® XNUMX åãšããŠå®çŸ©ããŸããã - ããŒãã« 1 ããã㯠+ åã€ã³ããã¯ã¹ã« 1 ããã㯠- 空ã®å¯èœæ§ãããããŒãã«ã®å Žåã äžå®éã®ããŒã¿ãåžžã«ãéåžžããããã¡ãŒã«æ®ããšäºæ³ãããå Žåã¯ããã®åŒã調æŽããã®ãåççã§ãã
æ€èšŒãªã¯ãšã¹ã
SELECT
relpages
, ((
SELECT
count(*)
FROM
pg_index
WHERE
indrelid = cl.oid
) + 1) << 13 size_norm -- ÑÑÑ Ð¿ÑавОлÑМее ЎелаÑÑ * current_setting('block_size')::bigint, МП кÑП ЌеМÑÐµÑ ÑÐ°Ð·ÐŒÐµÑ Ð±Ð»ÐŸÐºÐ°?..
, pg_total_relation_size(oid) size
, coalesce(extract('epoch' from (now() - greatest(
pg_stat_get_last_vacuum_time(oid)
, pg_stat_get_last_autovacuum_time(oid)
))), 1 << 30) vaclag
FROM
pg_class cl
WHERE
oid = $1::regclass -- tbl
LIMIT 1;
relpages | size_norm | size | vaclag
-------------------------------------------
0 | 24576 | 1105920 | 3392.484835
#1: ç空ã®ãŸãŸ
䞊åã¯ãšãªãé倧ãªåŠšãã«ãªã£ãŠãããã©ãããã€ãŸã䞊åã¯ãšãªã®éå§ä»¥æ¥ãæ£ç¢ºã«äœä»¶ã®ã¬ã³ãŒãããå€ããªã£ããã®ããäºåã«ç¥ãããšã¯ã§ããŸããã ãããã£ãŠãäœããã®æ¹æ³ã§ããŒãã«ãåŠçããããšã«ããå Žåã¯ããããã®å Žåã§ããæåã«ããŒãã«ã«å¯ŸããŠå®è¡ããå¿ èŠããããŸãã VACUUM - VACUUM FULL ãšã¯ç°ãªããèªã¿åã/æžã蟌ã¿ããŒã¿ãåŠçãã䞊åããã»ã¹ã劚ããŸããã
åæã«ãåé€ããããã®ã®ã»ãšãã©ãããã«åé€ã§ããŸãã ã¯ãããã®ããŒãã«ã«å¯ŸããåŸç¶ã®ã¯ãšãªã¯ç§ãã¡ã«éãããŸãã ãããããã£ãã·ã¥ãã«ããããã«ããããã®æéãççž®ããããµãŒãã¹ ãã©ã³ã¶ã¯ã·ã§ã³ã«ãã£ãŠä»ã®ãŠãŒã¶ãŒããããã¯ããåèšæéãççž®ãããŸãã
#2: 誰ã家ã«ããŸãã?
ããŒãã«ã«äœãããããã©ããã確èªããŠã¿ãŸãããã
TABLE tbl LIMIT 1;
ã¬ã³ãŒãã XNUMX ã€ãæ®ã£ãŠããªãå Žåã¯ã次ã®ããã«ããã ãã§åŠçã倧å¹
ã«ç¯çŽã§ããŸãã
ããã¯åããŒãã«ã«å¯Ÿããç¡æ¡ä»¶ã® DELETE ã³ãã³ããšåãããã«åäœããŸãããå®éã«ããŒãã«ãã¹ãã£ã³ããªããããã¯ããã«é«éã§ãã ããã«ãããã«ãã£ã¹ã¯é åã解æŸããããããåŸã§ VACUUM æäœãå®è¡ããå¿ èŠã¯ãããŸããã
ããŒãã« ã·ãŒã±ã³ã¹ ã«ãŠã³ã¿ãŒ (RESTART IDENTITY) ããªã»ããããå¿ èŠããããã©ããã¯ããŠãŒã¶ãŒã決å®ããŸãã
#3: å šå¡ã§äº€ä»£ããŠãã ãã!
ç§ãã¡ã¯éåžžã«ç«¶äºã®æ¿ããç°å¢ã§åããŠãããããããŒãã«ã«ãšã³ããªããªãããšã確èªããŠããéã«ã誰ãããã§ã«ããã«äœããæžã蟌ãã§ããå¯èœæ§ããããŸãã ãã®æ å ±ã倱ãã¹ãã§ã¯ãããŸãããããã§ã¯ã©ãã§ãããã? ããã§ãã誰ãããã確å®ã«æžãçããããšãã§ããªãããã«ããå¿ èŠããããŸãã
ãããè¡ãã«ã¯ãæå¹ã«ããå¿ èŠããããŸã ã·ãªã¢ã«åå¯èœ-ãã©ã³ã¶ã¯ã·ã§ã³ãåé¢ãïŒã¯ããããã§ãã©ã³ã¶ã¯ã·ã§ã³ãéå§ããŸãïŒãããŒãã«ãããã£ãããšãããã¯ããŸãã
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
LOCK TABLE tbl IN ACCESS EXCLUSIVE MODE;
ãã®ãããã¯ã®ã¬ãã«ã¯ããããã¯ã«å¯ŸããŠå®è¡ããæäœã«ãã£ãŠæ±ºãŸããŸãã
#4: å©ççžå
ç§ãã¡ã¯ããã«æ¥ãŠãæšèããããã¯ãããããšèããŠããŸããããšãã°ããã®ç¬éã«èª°ããæšèãèªãã§ããå Žåã¯ã©ããªãã§ãããã? ç§ãã¡ã¯ãã®ãããã¯ã解æŸãããã®ãããã³ã°ã¢ãããããŠåŸ ã£ãŠãããšãèªã¿ããä»ã®äººãç§ãã¡ã«ééããã§ããã...
ãããèµ·ãããªãããã«ããããã«ãç§ãã¡ã¯ãèªåèªèº«ãç ç²ã«ãããããšã«ãªããŸããç¹å®ã®ïŒèš±å®¹ã§ããçãïŒæéå ã«ããã¯ãååŸã§ããªãã£ãå ŽåãããŒã¹ããäŸå€ãåãåããŸãããå°ãªããšãããŸãå¹²æžããŸããããã®ä»ã
ãããè¡ãã«ã¯ãã»ãã·ã§ã³å€æ°ãèšå®ããŸãã
SET statement_timeout = ...;LOCK TABLE ...;
åŸã§å€æ°ã®ãå€ããå€ã埩å ããå¿ èŠããªãããã«ã次ã®åœ¢åŒã䜿çšããŸãã ããŒã«ã«ã«èšå®ãèšå®ã®ç¯å²ãçŸåšã®ãã©ã³ã¶ã¯ã·ã§ã³ã«å¶éããŸãã
ããŒãã«ã«å€§éã®ããŒã¿ãããå Žåã«ãã©ã³ã¶ã¯ã·ã§ã³ã蚱容ã§ããªãå€ã«ãŸã§æ¡åŒµã§ããªãããã«ãstatement_timeout ã¯åŸç¶ã®ãã¹ãŠã®ãªã¯ãšã¹ãã«é©çšãããããšãæãåºããŠãã ããã
#5: ããŒã¿ãã³ããŒãã
ããŒãã«ãå®å šã«ç©ºã§ãªãå Žåã¯ãè£å©äžæããŒãã«ã䜿çšããŠããŒã¿ãåä¿åããå¿ èŠããããŸãã
CREATE TEMPORARY TABLE _tmp_swap ON COMMIT DROP AS TABLE tbl;
ãµã€ã³ ã³ãããããããæ ããã¯ããã©ã³ã¶ã¯ã·ã§ã³ãçµäºããæç¹ã§äžæããŒãã«ãååšããªããªããããæ¥ç¶ã³ã³ããã¹ãã§æåã§ããŒãã«ãåé€ããå¿ èŠããªãããšãæå³ããŸãã
ãã©ã€ããããŒã¿ã¯ããã»ã©å€ããªããšæ³å®ããŠããããããã®æäœã¯éåžžã«è¿ éã«å®è¡ãããã¯ãã§ãã
ããŠãããã ãã§ãïŒ ååŒå®äºåŸãå¿ããã«
æçµçãªã¹ã¯ãªããããŸãšãã
ãã®ãç䌌 Pythonãã䜿çšããŸãã
# ÑПбОÑаеЌ ÑÑаÑОÑÑÐžÐºÑ Ñ ÑаблОÑÑ
stat <-
SELECT
relpages
, ((
SELECT
count(*)
FROM
pg_index
WHERE
indrelid = cl.oid
) + 1) << 13 size_norm
, pg_total_relation_size(oid) size
, coalesce(extract('epoch' from (now() - greatest(
pg_stat_get_last_vacuum_time(oid)
, pg_stat_get_last_autovacuum_time(oid)
))), 1 << 30) vaclag
FROM
pg_class cl
WHERE
oid = $1::regclass -- table_name
LIMIT 1;
# ÑаблОÑа бПлÑÑе ÑелевПгП ÑазЌеÑа О VACUUM бÑл ЎавМП
if stat.size > 2 * stat.size_norm and stat.vaclag is None or stat.vaclag > 60:
-> VACUUM %table;
try:
-> BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# пÑÑаеЌÑÑ Ð·Ð°Ñ
ваÑОÑÑ ÐŒÐŸÐœÐŸÐ¿ÐŸÐ»ÑÐœÑÑ Ð±Ð»ÐŸÐºÐžÑÐŸÐ²ÐºÑ Ñ Ð¿ÑеЎелÑÐœÑÐŒ вÑеЌеМеЌ ÐŸÐ¶ÐžÐŽÐ°ÐœÐžÑ 1s
-> SET LOCAL statement_timeout = '1s'; SET LOCAL lock_timeout = '1s';
-> LOCK TABLE %table IN ACCESS EXCLUSIVE MODE;
# МаЎП ÑбеЎОÑÑÑÑ Ð² пÑÑÑПÑе ÑаблОÑÑ Ð²ÐœÑÑÑО ÑÑаМзакÑОО Ñ Ð±Ð»ÐŸÐºÐžÑПвкПй
row <- TABLE %table LIMIT 1;
# еÑлО в ÑаблОÑе ÐœÐµÑ ÐœÐž ПЎМПй "жОвПй" запОÑО - ПÑОÑаеЌ ее пПлМПÑÑÑÑ, в пÑПÑОвМПЌ ÑлÑÑае - "пеÑевÑÑавлÑеЌ" вÑе запОÑО ÑеÑез вÑеЌеММÑÑ ÑаблОÑÑ
if row is None:
-> TRUNCATE TABLE %table RESTART IDENTITY;
else:
# ÑПзЎаеЌ вÑеЌеММÑÑ ÑаблОÑÑ Ñ ÐŽÐ°ÐœÐœÑЌО ÑаблОÑÑ-ПÑОгОМала
-> CREATE TEMPORARY TABLE _tmp_swap ON COMMIT DROP AS TABLE %table;
# ПÑОÑаеЌ ПÑОгОМал без ÑбÑПÑа пПÑлеЎПваÑелÑМПÑÑО
-> TRUNCATE TABLE %table;
# вÑÑавлÑеЌ вÑе ÑПÑ
ÑаМеММÑе вП вÑеЌеММПй ÑаблОÑе ЎаММÑе ПбÑаÑМП
-> INSERT INTO %table TABLE _tmp_swap;
-> COMMIT;
except Exception as e:
# еÑлО ÐŒÑ Ð¿ÐŸÐ»ÑÑОлО ПÑОбкÑ, МП ÑПеЎОМеМОе вÑе еÑе "жОвП" - ÑлПвОлО ÑайЌаÑÑ
if not isinstance(e, InterfaceError):
-> ROLLBACK;
ããŒã¿ãå床ã³ããŒããªãããã«ããããšã¯ã§ããŸãã?ååãšããŠãããŒãã«èªäœã® oid ã BL åŽã®ä»ã®ã¢ã¯ãã£ããã£ã DB åŽã® FK ã«é¢é£ä»ããããŠããªãå Žåã¯å¯èœã§ãã
CREATE TABLE _swap_%table(LIKE %table INCLUDING ALL);
INSERT INTO _swap_%table TABLE %table;
DROP TABLE %table;
ALTER TABLE _swap_%table RENAME TO %table;
ãœãŒã¹ããŒãã«ã§ã¹ã¯ãªãããå®è¡ããã¡ããªã¯ã¹ã確èªããŠã¿ãŸãããã
VACUUM tbl;
BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SET LOCAL statement_timeout = '1s'; SET LOCAL lock_timeout = '1s';
LOCK TABLE tbl IN ACCESS EXCLUSIVE MODE;
CREATE TEMPORARY TABLE _tmp_swap ON COMMIT DROP AS TABLE tbl;
TRUNCATE TABLE tbl;
INSERT INTO tbl TABLE _tmp_swap;
COMMIT;
relpages | size_norm | size | vaclag
-------------------------------------------
0 | 24576 | 49152 | 32.705771
ãã¹ãŠããŸããããŸããïŒ ããŒãã«ã¯ 50 åã® XNUMX ã«çž®å°ããããã¹ãŠã® UPDATE ãåã³é«éã«å®è¡ãããããã«ãªããŸããã
åºæïŒ habr.com