Когда пасуСт VACUUM β€” чистим Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ

VACUUM ΠΌΠΎΠΆΠ΅Ρ‚ Β«Π·Π°Ρ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒΒ» ΠΈΠ· Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ Π² PostgreSQL Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Π½ΠΈΠΊΡ‚ΠΎ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΡƒΠ²ΠΈΠ΄Π΅Ρ‚ΡŒ β€” Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ Π½Π΅Ρ‚ Π½ΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎΠ³ΠΎ запроса, ΡΡ‚Π°Ρ€Ρ‚ΠΎΠ²Π°Π²ΡˆΠ΅Π³ΠΎ Ρ€Π°Π½ΡŒΡˆΠ΅, Ρ‡Π΅ΠΌ эти записи Π±Ρ‹Π»ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Ρ‹.

А Ссли Ρ‚Π°ΠΊΠΎΠΉ нСприятный Ρ‚ΠΈΠΏ (ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚Π΅Π»ΡŒΠ½Π°Ρ OLAP-Π½Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π½Π° OLTP-Π±Π°Π·Π΅) всС ΠΆΠ΅ Π΅ΡΡ‚ΡŒ? Как ΠΏΠΎΡ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎ ΠΌΠ΅Π½ΡΡŽΡ‰ΡƒΡŽΡΡ Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Π² ΠΎΠΊΡ€ΡƒΠΆΠ΅Π½ΠΈΠΈ Π΄Π»ΠΈΠ½Π½Ρ‹Ρ… запросов ΠΈ Π½Π΅ Π½Π°ΡΡ‚ΡƒΠΏΠΈΡ‚ΡŒ Π½Π° Π³Ρ€Π°Π±Π»ΠΈ?

Когда пасуСт VACUUM — чистим Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ

РаскладываСм Π³Ρ€Π°Π±Π»ΠΈ

Π‘Π½Π°Ρ‡Π°Π»Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ, Π² Ρ‡Π΅ΠΌ ΠΆΠ΅ Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ ΠΈ ΠΊΠ°ΠΊ Π²ΠΎΠΎΠ±Ρ‰Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ½ΡƒΡ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ°, ΠΊΠΎΡ‚ΠΎΡ€ΡƒΡŽ ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ Ρ€Π΅ΡˆΠΈΡ‚ΡŒ.

ΠžΠ±Ρ‹Ρ‡Π½ΠΎ такая ситуация случаСтся Π½Π° ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ нСбольшой Ρ‚Π°Π±Π»ΠΈΡ†Π΅, Π½ΠΎ Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ происходит ΠΎΡ‡Π΅Π½ΡŒ ΠΌΠ½ΠΎΠ³ΠΎ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ. ΠžΠ±Ρ‹Ρ‡Π½ΠΎ это ΠΈΠ»ΠΈ Ρ€Π°Π·Π½Ρ‹Π΅ счСтчики/Π°Π³Ρ€Π΅Π³Π°Ρ‚Ρ‹/Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ΠΈ, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… часто-часто выполняСтся 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);

Π’Π΅ΠΏΠ΅Ρ€ΡŒ ΠΌΡ‹ ΠΌΠ½ΠΎΠ³ΠΎ-ΠΌΠ½ΠΎΠ³ΠΎ Ρ€Π°Π· обновляСм Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈΠ· счСтчиков. Для чистоты экспСримСнта сдСлаСм это Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Ρ… транзакциях с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ dblink, ΠΊΠ°ΠΊ это Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΡ€ΠΎΠΈΡΡ…ΠΎΠ΄ΠΈΡ‚ΡŒ Π² Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ:

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.524ms Π΄ΠΎ 3.808ms? Π”Π° ΠΈ Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ наш строится всС ΠΌΠ΅Π΄Π»Π΅Π½Π½Π΅Π΅ ΠΈ ΠΌΠ΅Π΄Π»Π΅Π½Π½Π΅Π΅.

Π’ΠΎ всСм Π²ΠΈΠ½ΠΎΠ²Π°Ρ‚ MVCC

ВсС Π΄Π΅Π»ΠΎ Π² ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌΠ΅ 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 сСкунд с послСднСго [auto]VACUUM.
  • физичСский Ρ€Π°Π·ΠΌΠ΅Ρ€ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ большС Ρ†Π΅Π»Π΅Π²ΠΎΠ³ΠΎ
    ΠžΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ Π΅Π³ΠΎ ΠΊΠ°ΠΊ ΡƒΠ΄Π²ΠΎΠ΅Π½Π½ΠΎΠ΅ количСство страниц (Π±Π»ΠΎΠΊΠΎΠ² ΠΏΠΎ 8KB) ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ минимального Ρ€Π°Π·ΠΌΠ΅Ρ€Π° β€” 1 blk Π½Π° heap + 1 blk Π½Π° ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΈΠ· индСксов β€” для ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»ΡŒΠ½ΠΎ-пустой Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹. Если ΠΆΠ΅ ΠΌΡ‹ ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ Π² Π±ΡƒΡ„Π΅Ρ€Π΅ Β«ΡˆΡ‚Π°Ρ‚Π½ΠΎΒ» Π±ΡƒΠ΄Π΅Ρ‚ всСгда ΠΎΡΡ‚Π°Π²Π°Ρ‚ΡŒΡΡ Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ объСм Π΄Π°Π½Π½Ρ‹Ρ…, эту Ρ„ΠΎΡ€ΠΌΡƒΠ»Ρƒ Ρ€Π°Π·ΡƒΠΌΠ½ΠΎ ΠΏΠΎΠ΄Ρ‚ΡŽΠ½ΠΈΡ‚ΡŒ.

ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΎΡ‡Π½Ρ‹ΠΉ запрос

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 β€” ΠΎΠ½, Π² ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠ΅ ΠΎΡ‚ VACUUM FULL, ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½Ρ‹ΠΌ процСссам Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с Π΄Π°Π½Π½Ρ‹ΠΌΠΈ Π½Π° Ρ‡Ρ‚Π΅Π½ΠΈΠ΅-запись Π½Π΅ ΠΌΠ΅ΡˆΠ°Π΅Ρ‚.

Π—Π°ΠΎΠ΄Π½ΠΎ ΠΎΠ½ ΠΌΠΎΠΆΠ΅Ρ‚ сразу Π²Ρ‹Ρ‡ΠΈΡΡ‚ΠΈΡ‚ΡŒ Π±ΠΎΠ»ΡŒΡˆΡƒΡŽ Ρ‡Π°ΡΡ‚ΡŒ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ Π±Ρ‹ ΡƒΠ±Ρ€Π°Ρ‚ΡŒ. Π”Π° ΠΈ ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ запросы ΠΏΠΎ этой Ρ‚Π°Π±Π»ΠΈΡ†Π΅ ΠΏΠΎΠΉΠ΄ΡƒΡ‚ Ρƒ нас ΠΏΠΎ «горячСму ΠΊΡΡˆΡƒΒ», Ρ‡Ρ‚ΠΎ сократит ΠΈΡ… ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ β€” Π°, Π·Π½Π°Ρ‡ΠΈΡ‚, ΠΈ суммарноС врСмя Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ Π΄Ρ€ΡƒΠ³ΠΈΡ… нашСй ΠΎΠ±ΡΠ»ΡƒΠΆΠΈΠ²Π°ΡŽΡ‰Π΅ΠΉ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠ΅ΠΉ.

#2: Π•ΡΡ‚ΡŒ ΠΊΡ‚ΠΎ-Π½ΠΈΠ±ΡƒΠ΄ΡŒ Π΄ΠΎΠΌΠ°?

Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ β€” Π΅ΡΡ‚ΡŒ Π»ΠΈ Π² Ρ‚Π°Π±Π»ΠΈΡ†Π΅ Π²ΠΎΠΎΠ±Ρ‰Π΅ Ρ…ΠΎΡ‚ΡŒ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ:

TABLE tbl LIMIT 1;

Если Π½Π΅ ΠΎΡΡ‚Π°Π»ΠΎΡΡŒ Π½ΠΈ Π΅Π΄ΠΈΠ½ΠΎΠΉ записи, Ρ‚ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ сильно ΡΡΠΊΠΎΠ½ΠΎΠΌΠΈΡ‚ΡŒ Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ β€” просто Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΠ² TRUNCATE:

Она дСйствуСт Ρ‚Π°ΠΊ ΠΆΠ΅, ΠΊΠ°ΠΊ бСзусловная ΠΊΠΎΠΌΠ°Π½Π΄Π° DELETE для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹, Π½ΠΎ Π³ΠΎΡ€Π°Π·Π΄ΠΎ быстрСС, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΠΎΠ½Π° фактичСски Π½Π΅ сканируСт Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹. Π‘ΠΎΠ»Π΅Π΅ Ρ‚ΠΎΠ³ΠΎ, ΠΎΠ½Π° Π½Π΅ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎ высвобоТдаСт дисковоС пространство, Ρ‚Π°ΠΊ Ρ‡Ρ‚ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΡŽ VACUUM послС Π½Π΅Ρ‘ Π½Π΅ трСбуСтся.

Надо Π»ΠΈ Π²Π°ΠΌ ΠΏΡ€ΠΈ этом ΡΠ±Ρ€Π°ΡΡ‹Π²Π°Ρ‚ΡŒ счСтчик ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ (RESTART IDENTITY) β€” Ρ€Π΅ΡˆΠ°ΠΉΡ‚Π΅ сами.

#3: ВсС β€” ΠΏΠΎ-ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ!

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΌΡ‹ Ρ€Π°Π±ΠΎΡ‚Π°Π΅ΠΌ Π² условиях высокой конкурСнтности, Ρ‚ΠΎ ΠΏΠΎΠΊΠ° ΠΌΡ‹ Ρ‚ΡƒΡ‚ провСряСм отсутствиС записСй Π² Ρ‚Π°Π±Π»ΠΈΡ†Π΅, ΠΊΡ‚ΠΎ-Ρ‚ΠΎ ΠΌΠΎΠ³ Ρ‚ΡƒΠ΄Π° ΡƒΠΆΠ΅ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π·Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ. ΠŸΠΎΡ‚Π΅Ρ€ΡΡ‚ΡŒ эту ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΌΡ‹ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹, Π·Π½Π°Ρ‡ΠΈΡ‚ β€” Ρ‡Ρ‚ΠΎ? ΠŸΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ, Π½Π°Π΄ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½ΠΈΠΊΡ‚ΠΎ ΡƒΠΆ Ρ‚ΠΎΡ‡Π½ΠΎ Π·Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ Π½Π΅ ΠΌΠΎΠ³.

Для этого Π½Π°ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ SERIALIZABLE-ΠΈΠ·ΠΎΠ»ΡΡ†ΠΈΡŽ для нашСй Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ (Π΄Π°, Ρ‚ΡƒΡ‚ ΠΌΡ‹ стартуСм Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΡŽ) ΠΈ Π·Π°Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ Β«Π½Π°ΠΌΠ΅Ρ€Ρ‚Π²ΠΎΒ»:

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
LOCK TABLE tbl IN ACCESS EXCLUSIVE MODE;

ИмСнно Ρ‚Π°ΠΊΠΎΠΉ ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ обусловлСн Ρ‚Π΅ΠΌΠΈ опСрациями, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΡ‹ Ρ…ΠΎΡ‚ΠΈΠΌ Π½Π°Π΄ Π½Π΅ΠΉ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚ΡŒ.

#4: ΠšΠΎΠ½Ρ„Π»ΠΈΠΊΡ‚ интСрСсов

ΠœΡ‹ Ρ‚ΡƒΡ‚ ΠΏΡ€ΠΈΡ…ΠΎΠ΄ΠΈΠΌ ΠΈ Ρ…ΠΎΡ‚ΠΈΠΌ Ρ‚Π°Π±Π»ΠΈΡ‡ΠΊΡƒ Β«Π·Π°Π»ΠΎΡ‡ΠΈΡ‚ΡŒΒ» β€” Π° Ссли Π½Π° Π½Π΅ΠΉ Π² этот ΠΌΠΎΠΌΠ΅Π½Ρ‚ ΠΊΡ‚ΠΎ-Ρ‚ΠΎ Π±Ρ‹Π» Π°ΠΊΡ‚ΠΈΠ²Π΅Π½, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Ρ‡ΠΈΡ‚Π°Π» ΠΈΠ· Π½Π΅Π΅? ΠœΡ‹ «повиснСм» Π² ΠΎΠΆΠΈΠ΄Π°Π½ΠΈΠΈ освобоТдСния этой Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ, Π° Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΆΠ΅Π»Π°ΡŽΡ‰ΠΈΠ΅ ΠΏΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ упрутся ΡƒΠΆΠ΅ Π² нас…

Π§Ρ‚ΠΎΠ±Ρ‹ Ρ‚Π°ΠΊΠΎΠ³ΠΎ Π½Π΅ ΠΏΡ€ΠΎΠΈΠ·ΠΎΡˆΠ»ΠΎ, Β«ΠΏΠΎΠΆΠ΅Ρ€Ρ‚Π²ΡƒΠ΅ΠΌ собой» β€” Ссли ΡƒΠΆ Π·Π° ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½ΠΎΠ΅ (допустимо ΠΌΠ°Π»ΠΎΠ΅) врСмя Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΡƒ Π½Π°ΠΌ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ всС-Ρ‚Π°ΠΊΠΈ Π½Π΅ ΡƒΠ΄Π°Π»ΠΎΡΡŒ, Ρ‚ΠΎ ΠΌΡ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠΌ ΠΎΡ‚ Π±Π°Π·Ρ‹ exception, Π½ΠΎ хотя Π±Ρ‹ Π½Π΅ помСшаСм сильно ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹ΠΌ.

Для этого выставим ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ сСссии lock_timeout (для вСрсий 9.3+) ΠΈΠ»ΠΈ/ΠΈ statement_timeout. Π“Π»Π°Π²Π½ΠΎΠ΅ ΠΏΠΎΠΌΠ½ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ statement_timeout примСняСтся Ρ‚ΠΎΠ»ΡŒΠΊΠΎ со ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π³ΠΎ statement. Π’ΠΎ Π΅ΡΡ‚ΡŒ Π²ΠΎΡ‚ Ρ‚Π°ΠΊ Π² склСйкС β€” Π½Π΅ Π·Π°Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚:

SET statement_timeout = ...;LOCK TABLE ...;

Π§Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ Π·Π°Π½ΠΈΠΌΠ°Ρ‚ΡŒΡΡ ΠΏΠΎΡ‚ΠΎΠΌ восстановлСниСм «старого» значСния ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Ρ„ΠΎΡ€ΠΌΡƒ SET LOCAL, которая ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΠ²Π°Π΅Ρ‚ ΠΎΠ±Π»Π°ΡΡ‚ΡŒ дСйствия настройки Ρ‚Π΅ΠΊΡƒΡ‰Π΅ΠΉ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠ΅ΠΉ.

Помним, Ρ‡Ρ‚ΠΎ statement_timeout распространяСтся Π½Π° всС ΠΏΠΎΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ запросы, Ρ‡Ρ‚ΠΎΠ±Ρ‹ транзакция Π½Π΅ ΠΌΠΎΠ³Π»Π° Ρƒ нас Ρ€Π°ΡΡ‚ΡΠ½ΡƒΡ‚ΡŒΡΡ Π΄ΠΎ Π½Π΅ΠΏΡ€ΠΈΠ΅ΠΌΠ»Π΅ΠΌΡ‹Ρ… Π²Π΅Π»ΠΈΡ‡ΠΈΠ½, Ссли Π΄Π°Π½Π½Ρ‹Ρ… Π² Ρ‚Π°Π±Π»ΠΈΡ†Π΅ Ρ‚Π°ΠΊΠΈ окаТСтся ΠΌΠ½ΠΎΠ³ΠΎ.

#5: ΠšΠΎΠΏΠΈΡ€ΡƒΠ΅ΠΌ Π΄Π°Π½Π½Ρ‹Π΅

Если Ρ‚Π°Π±Π»ΠΈΡ†Π° оказалась Π½Π΅ совсСм пустая β€” Π΄Π°Π½Π½Ρ‹Π΅ придСтся ΠΏΠ΅Ρ€Π΅ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒ Ρ‡Π΅Ρ€Π΅Π· Π²ΡΠΏΠΎΠΌΠΎΠ³Π°Ρ‚Π΅Π»ΡŒΠ½ΡƒΡŽ Π²Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ Ρ‚Π°Π±Π»ΠΈΡ‡ΠΊΡƒ:

CREATE TEMPORARY TABLE _tmp_swap ON COMMIT DROP AS TABLE tbl;

Π‘ΠΈΠ³Π½Π°Ρ‚ΡƒΡ€Π° ON COMMIT DROP ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚ окончания Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ врСмСнная Ρ‚Π°Π±Π»ΠΈΡ†Π° пСрСстанСт ΡΡƒΡ‰Π΅ΡΡ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ, ΠΈ Π·Π°Π½ΠΈΠΌΠ°Ρ‚ΡŒΡΡ Π΅Π΅ Ρ€ΡƒΡ‡Π½Ρ‹ΠΌ ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ΠΌ Π² контСкстС соСдинСния Π½Π΅ Π½ΡƒΠΆΠ½ΠΎ.

ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ ΠΌΡ‹ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ Β«ΠΆΠΈΠ²Ρ‹Ρ…Β» Π΄Π°Π½Π½Ρ‹Ρ… Π½Π΅ ΠΎΡ‡Π΅Π½ΡŒ ΠΌΠ½ΠΎΠ³ΠΎ, Ρ‚ΠΎ эта опСрация Π΄ΠΎΠ»ΠΆΠ½Π° ΠΏΡ€ΠΎΠΉΡ‚ΠΈ достаточно быстро.

Ну Π²ΠΎΡ‚ ΠΊΠ°ΠΊ Π±Ρ‹ ΠΈ всС! НС Π·Π°Π±Ρ‹Π²Π°ΠΉΡ‚Π΅ послС Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ Π·Π°ΠΏΡƒΡΡ‚ΠΈΡ‚ΡŒ ANALYZE для Π½ΠΎΡ€ΠΌΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ статистики Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹, Ссли это Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ.

Π‘ΠΎΠ±ΠΈΡ€Π°Π΅ΠΌ ΠΈΡ‚ΠΎΠ³ΠΎΠ²Ρ‹ΠΉ скрипт

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Ρ‚Π°ΠΊΠΎΠΉ «псСвдопитон»:

# собираСм статистику с Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹
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 самой Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ Π½Π΅ завязаны ΠΊΠ°ΠΊΠΈΠ΅-Ρ‚ΠΎ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ активности со стороны Π‘Π› ΠΈΠ»ΠΈ 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 Ρ€Π°Π·, ΠΈ всС UPDATE снова Π±Π΅Π³Π°ΡŽΡ‚ быстро.

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