PostgreSQL Antipatterns: мСняСм Π΄Π°Π½Π½Ρ‹Π΅ Π² ΠΎΠ±Ρ…ΠΎΠ΄ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Π°

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

НапримСр, Π½Π° Ρ‚Π°Π±Π»ΠΈΡ†Π΅, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π²Π°ΠΌ Π½Π°Π΄ΠΎ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ ΠΏΠΎΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ, висит Π·Π»ΠΎΠ±Π½Ρ‹ΠΉ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€ ON UPDATE, пСрСносящий всС измСнСния Π² ΠΊΠ°ΠΊΠΈΠ΅-Π½ΠΈΠ±ΡƒΠ΄ΡŒ Π°Π³Ρ€Π΅Π³Π°Ρ‚Ρ‹. А Π²Π°ΠΌ Π½Π°Π΄ΠΎ всС ΠΏΠΎΠΎΠ±Π½ΠΎΠ²Π»ΡΡ‚ΡŒ (Π½ΠΎΠ²ΠΎΠ΅ ΠΏΠΎΠ»Π΅ ΠΏΡ€ΠΎΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€) Ρ‚Π°ΠΊ Π°ΠΊΠΊΡƒΡ€Π°Ρ‚Π½ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ эти Π°Π³Ρ€Π΅Π³Π°Ρ‚Ρ‹ Π½Π΅ Π·Π°Ρ‚Ρ€ΠΎΠ½ΡƒΠ»ΠΈΡΡŒ.

Π”Π°Π²Π°ΠΉΡ‚Π΅ просто ΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΠΌ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Ρ‹!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
  UPDATE ...; -- Ρ‚ΡƒΡ‚ Π΄ΠΎΠ»Π³ΠΎ-Π΄ΠΎΠ»Π³ΠΎ
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

БобствСнно, Ρ‚ΡƒΡ‚ ΠΈ всС β€” всС ΡƒΠΆΠ΅ висит.

ΠŸΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ ALTER TABLE Π½Π°ΠΊΠ»Π°Π΄Ρ‹Π²Π°Π΅Ρ‚ AccessExclusive-Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΡƒ, ΠΏΠΎΠ΄ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ Π½ΠΈΠΊΡ‚ΠΎ ΠΏΠ°Ρ€Π°Π»Π»Π΅Π»ΡŒΠ½ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡŽΡ‰ΠΈΠΉΡΡ, Π΄Π°ΠΆΠ΅ простой SELECT, Π½ΠΈΡ‡Π΅Π³ΠΎ ΠΈΠ· Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π½Π΅ смоТСт. Π’ΠΎ Π΅ΡΡ‚ΡŒ ΠΏΠΎΠΊΠ° эта транзакция Π½Π΅ закончится, всС ΠΆΠ΅Π»Π°ΡŽΡ‰ΠΈΠ΅ Π΄Π°ΠΆΠ΅ «просто ΠΏΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒΒ» Π±ΡƒΠ΄ΡƒΡ‚ ΠΆΠ΄Π°Ρ‚ΡŒ. А ΠΌΡ‹ ΠΏΠΎΠΌΠ½ΠΈΠΌ, Ρ‡Ρ‚ΠΎ UPDATE Ρƒ нас Π΄ΠΎ-ΠΎ-олгий…

Π”Π°Π²Π°ΠΉΡ‚Π΅ Ρ‚ΠΎΠ³Π΄Π° быстро ΠΎΡ‚ΠΊΠ»ΡŽΡ‡ΠΈΠΌ, ΠΏΠΎΡ‚ΠΎΠΌ быстро Π²ΠΊΠ»ΡŽΡ‡ΠΈΠΌ!

BEGIN;
  ALTER TABLE ... DISABLE TRIGGER ...;
COMMIT;

UPDATE ...;

BEGIN;
  ALTER TABLE ... ENABLE TRIGGER ...;
COMMIT;

Π’ΡƒΡ‚ ситуация ΡƒΠΆΠ΅ Π»ΡƒΡ‡ΡˆΠ΅, врСмя оТидания сущСствСнно мСньшС. Но всСго Π΄Π²Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ портят всю красоту:

  • ALTER TABLE сам ΠΆΠ΄Π΅Ρ‚ всС Π΄Ρ€ΡƒΠ³ΠΈΠ΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ Π½Π° Ρ‚Π°Π±Π»ΠΈΡ†Π΅, Π²ΠΊΠ»ΡŽΡ‡Π°Ρ Π΄Π»ΠΈΠ½Π½Ρ‹Π΅ SELECT
  • Пока Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€ Π²Ρ‹ΠΊΠ»ΡŽΡ‡Π΅Π½, Β«ΠΏΡ€ΠΎΠ»Π΅Ρ‚ΠΈΡ‚ ΠΌΠΈΠΌΠΎΒ» любоС ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π² Ρ‚Π°Π±Π»ΠΈΡ†Π΅, Π΄Π°ΠΆΠ΅ Π½Π΅ нашС. И Π² Π°Π³Ρ€Π΅Π³Π°Ρ‚Ρ‹ Π½Ρƒ Π½ΠΈΠΊΠ°ΠΊ Π½Π΅ ΠΏΠΎΠΏΠ°Π΄Π΅Ρ‚, хотя ΠΈ Π΄ΠΎΠ»ΠΆΠ½ΠΎ. Π‘Π΅Π΄Π°!

Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΌΠΈ сСссии

Π˜Ρ‚Π°ΠΊ, Π½Π° ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π΅ ΠΌΡ‹ Π½Π°Ρ‚ΠΊΠ½ΡƒΠ»ΠΈΡΡŒ Π½Π° ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ β€” Π½Π°Π΄ΠΎ ΠΊΠ°ΠΊ-Ρ‚ΠΎ Π½Π°ΡƒΡ‡ΠΈΡ‚ΡŒ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€ ΠΎΡ‚Π»ΠΈΡ‡Π°Ρ‚ΡŒ «наши» измСнСния Π² Ρ‚Π°Π±Π»ΠΈΡ†Π΅ ΠΎΡ‚ Β«Π½Π΅ Π½Π°ΡˆΠΈΡ…Β». «Наши» ΠΏΡ€ΠΎΠΏΡƒΡΠΊΠ°Ρ‚ΡŒ ΠΊΠ°ΠΊ Π΅ΡΡ‚ΡŒ, Π° Π½Π° Β«Π½Π΅ наши» β€” ΡΡ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ. Для этого ΠΌΠΎΠΆΠ½ΠΎ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΌΠΈ сСссии.

session_replication_role

Π§ΠΈΡ‚Π°Π΅ΠΌ ΠΌΠ°Π½ΡƒΠ°Π»:

На ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ срабатывания Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€ΠΎΠ² Ρ‚Π°ΠΊΠΆΠ΅ влияСт конфигурационная пСрСмСнная session_replication_role. Π’ΠΊΠ»ΡŽΡ‡Ρ‘Π½Π½Ρ‹Π΅ Π±Π΅Π· Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹Ρ… ΡƒΠΊΠ°Π·Π°Π½ΠΈΠΉ (ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ) Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Ρ‹ Π±ΡƒΠ΄ΡƒΡ‚ ΡΡ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ, ΠΊΠΎΠ³Π΄Π° Ρ€ΠΎΠ»ΡŒ Ρ€Π΅ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΠΈ β€” Β«originΒ» (ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ) ΠΈΠ»ΠΈ Β«localΒ». Π’Ρ€ΠΈΠ³Π³Π΅Ρ€Ρ‹, Π²ΠΊΠ»ΡŽΡ‡Ρ‘Π½Π½Ρ‹Π΅ ΡƒΠΊΠ°Π·Π°Π½ΠΈΠ΅ΠΌ ENABLE REPLICA, Π±ΡƒΠ΄ΡƒΡ‚ ΡΡ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ, Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ссли Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ Ρ€Π΅ΠΆΠΈΠΌ сСанса β€” Β«replicaΒ», Π° Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Ρ‹, Π²ΠΊΠ»ΡŽΡ‡Ρ‘Π½Π½Ρ‹Π΅ ΡƒΠΊΠ°Π·Π°Π½ΠΈΠ΅ΠΌ ENABLE ALWAYS, Π±ΡƒΠ΄ΡƒΡ‚ ΡΡ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ нСзависимо ΠΎΡ‚ Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ Ρ€Π΅ΠΆΠΈΠΌΠ° Ρ€Π΅ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΠΈ.

Особо ΠΏΠΎΠ΄Ρ‡Π΅Ρ€ΠΊΠ½Ρƒ, Ρ‡Ρ‚ΠΎ настройка относится ΠΊ Π½Π΅ ΠΊΠΎ всСм-всСм сразу, ΠΊΠ°ΠΊ ALTER TABLE, Π° Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΊ Π½Π°ΡˆΠ΅ΠΌΡƒ ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎΠΌΡƒ спСц-ΠΊΠΎΠ½Π½Π΅ΠΊΡ‚Ρƒ. Π˜Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π½Π΅ срабатывали Π½ΠΈΠΊΠ°ΠΊΠΈΠ΅ ΠΏΡ€ΠΈΠΊΠ»Π°Π΄Π½Ρ‹Π΅ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Ρ‹:

SET session_replication_role = replica; -- Π²Ρ‹ΠΊΠ»ΡŽΡ‡ΠΈΠ»ΠΈ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Ρ‹
UPDATE ...;
SET session_replication_role = DEFAULT; -- Π²Π΅Ρ€Π½ΡƒΠ»ΠΈ Π² исходноС состояниС

УсловиС Π²Π½ΡƒΡ‚Ρ€ΠΈ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Π°

Но ΠΏΡ€ΠΈΠ²Π΅Π΄Π΅Π½Π½Ρ‹ΠΉ Π²Ρ‹ΡˆΠ΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ для всСх Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€ΠΎΠ² сразу (ΠΈΠ»ΠΈ Π½Π°Π΄ΠΎ Β«Π°Π»ΡŒΡ‚Π΅Ρ€ΠΈΡ‚ΡŒΒ» Π·Π°Ρ€Π°Π½Π΅Π΅ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π½Π΅ хочСтся ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ). А Ссли Π½Π°ΠΌ Π½Π°Π΄ΠΎ Β«Π²Ρ‹ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒΒ» ΠΎΠ΄ΠΈΠ½ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€?

Π’ этом Π½Π°ΠΌ ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚ Β«ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠ°ΡΒ» пСрСмСнная сСссии:

ИмСна ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠΉ Π·Π°ΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‚ΡΡ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ: имя Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ, Ρ‚ΠΎΡ‡ΠΊΠ° ΠΈ Π·Π°Ρ‚Π΅ΠΌ собствСнно имя ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°, ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΎ ΠΏΠΎΠ»Π½Ρ‹ΠΌ ΠΈΠΌΠ΅Π½Π°ΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² Π² SQL. НапримСр: plpgsql.variable_conflict.
Π’Π°ΠΊ ΠΊΠ°ΠΊ внСсистСмныС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ установлСны Π² процСссах, Π½Π΅ Π·Π°Π³Ρ€ΡƒΠΆΠ°ΡŽΡ‰ΠΈΡ… ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ, PostgreSQL ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ значСния для Π»ΡŽΠ±Ρ‹Ρ… ΠΈΠΌΡ‘Π½ с двумя ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°ΠΌΠΈ.

Π‘Π½Π°Ρ‡Π°Π»Π° Π΄ΠΎΡ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌ Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€, ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ Ρ‚Π°ΠΊ:

BEGIN
    -- процСссу ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π°Ρ†ΠΈΠΈ ΠΌΠΎΠΆΠ½ΠΎ Π΄Π΅Π»Π°Ρ‚ΡŒ всС
    IF current_setting('mycfg.my_table_convert_process') = 'TRUE' THEN
        IF TG_OP IN ('INSERT', 'UPDATE') THEN
            RETURN NEW;
        ELSE
            RETURN OLD;
        END IF;
    END IF;
...

ΠšΡΡ‚Π°Ρ‚ΠΈ, это ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Β«Π½Π°ΠΆΠΈΠ²ΡƒΡŽΒ», Π±Π΅Π· Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΎΠΊ, Ρ‡Π΅Ρ€Π΅Π· CREATE OR REPLACE для Ρ‚Ρ€ΠΈΠ³Π³Π΅Ρ€Π½ΠΎΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ. А ΠΏΠΎΡ‚ΠΎΠΌ Π² спСц-ΠΊΠΎΠ½Π½Π΅ΠΊΡ‚Π΅ Π²Π·Π²ΠΎΠ΄ΠΈΠΌ «свою» ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΡƒΡŽ:


SET mycfg.my_table_convert_process = 'TRUE';
UPDATE ...;
SET mycfg.my_table_convert_process = ''; -- Π²Π΅Ρ€Π½ΡƒΠ»ΠΈ Π² исходноС состояниС

Π—Π½Π°Π΅Ρ‚Π΅ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ способы? ΠŸΠΎΠ΄Π΅Π»ΠΈΡ‚Π΅ΡΡŒ Π² коммСнтариях.

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