PostgreSQL Antipatterns: промяна Π½Π° Π΄Π°Π½Π½ΠΈ, заобикаляйки Ρ‚Ρ€ΠΈΠ³Π΅Ρ€

Π Π°Π½ΠΎ ΠΈΠ»ΠΈ късно ΠΌΠ½ΠΎΠ·ΠΈΠ½Π° сС ΡΠ±Π»ΡŠΡΠΊΠ²Π°Ρ‚ с нСобходимостта масово Π΄Π° ΠΊΠΎΡ€ΠΈΠ³ΠΈΡ€Π°Ρ‚ Π½Π΅Ρ‰ΠΎ Π² записитС Π½Π° Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π°. Π’Π΅Ρ‡Π΅ ΠΈΠΌΠ°ΠΌ ΠΊΠ°ΠΆΠΈ ΠΌΠΈ ΠΊΠ°ΠΊ Π΄Π° Π³ΠΎ направя ΠΏΠΎ-Π΄ΠΎΠ±Ρ€Π΅, Π° ΠΊΠ°ΠΊ - ΠΏΠΎ-Π΄ΠΎΠ±Ρ€Π΅ Π½Π΅ Π³ΠΎ ΠΏΡ€Π°Π²Π΅Ρ‚Π΅. ДнСс Ρ‰Π΅ говоря Π·Π° втория аспСкт Π½Π° масовата актуализация - относно Ρ‚Ρ€ΠΈΠ³Π΅Ρ€ΠΈΡ‚Π΅.

НапримСр, Π½Π° маса, Π² която трябва Π΄Π° ΠΏΠΎΠΏΡ€Π°Π²ΠΈΡ‚Π΅ Π½Π΅Ρ‰ΠΎ, виси зъл ΡΠΏΡƒΡΡŠΠΊ ON UPDATE, ΠΏΡ€Π΅Ρ…Π²ΡŠΡ€Π»ΡΠΉΠΊΠΈ всички ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈ към някои Π°Π³Ρ€Π΅Π³Π°Ρ‚ΠΈ. И трябва Π΄Π° Π°ΠΊΡ‚ΡƒΠ°Π»ΠΈΠ·ΠΈΡ€Π°Ρ‚Π΅ всичко (ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€Π°Ρ‚Π΅ Π½ΠΎΠ²ΠΎ ΠΏΠΎΠ»Π΅, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€) Ρ‚ΠΎΠ»ΠΊΠΎΠ²Π° Π²Π½ΠΈΠΌΠ°Ρ‚Π΅Π»Π½ΠΎ, Ρ‡Π΅ Ρ‚Π΅Π·ΠΈ Π°Π³Ρ€Π΅Π³Π°Ρ‚ΠΈ Π΄Π° Π½Π΅ Π±ΡŠΠ΄Π°Ρ‚ засСгнати.

НСка просто Π΄Π΅Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€Π°ΠΌΠ΅ Ρ‚Ρ€ΠΈΠ³Π΅Ρ€ΠΈΡ‚Π΅!

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

Π’ΡΡŠΡ‰Π½ΠΎΡΡ‚ Ρ‚ΠΎΠ²Π° Π΅ всичко - всичко виси.

Π·Π°Ρ‰ΠΎΡ‚ΠΎ ALTER TABLE Π½Π°Π»Π°Π³Π° Π˜Π·ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»Π΅Π½ Π΄ΠΎΡΡ‚ΡŠΠΏ- ΠΊΠ»ΡŽΡ‡Π°Π»ΠΊΠ°, ΠΏΠΎΠ΄ която Π½ΠΈΠΊΠΎΠΉ Π½Π΅ Ρ€Π°Π±ΠΎΡ‚ΠΈ ΠΏΠ°Ρ€Π°Π»Π΅Π»Π½ΠΎ, Π΄ΠΎΡ€ΠΈ ΠΈ ΠΎΠ±ΠΈΠΊΠ½ΠΎΠ²Π΅Π½ SELECT, няма Π΄Π° ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΏΡ€ΠΎΡ‡Π΅Ρ‚Π΅ Π½ΠΈΡ‰ΠΎ ΠΎΡ‚ Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π°. ВоСст, Π΄ΠΎΠΊΠ°Ρ‚ΠΎ Ρ‚Π°Π·ΠΈ транзакция ΠΏΡ€ΠΈΠΊΠ»ΡŽΡ‡ΠΈ, всСки, ΠΊΠΎΠΉΡ‚ΠΎ иска Π΄ΠΎΡ€ΠΈ β€žΡΠ°ΠΌΠΎ Π΄Π° Ρ‡Π΅Ρ‚Π΅β€œ, Ρ‰Π΅ Ρ‡Π°ΠΊΠ°. И Π½ΠΈΠ΅ ΠΏΠΎΠΌΠ½ΠΈΠΌ Ρ‚ΠΎΠ²Π° UPDATE ΠΈΠΌΠ°ΠΌΠ΅ дълго...

Π₯Π°ΠΉΠ΄Π΅ Π±ΡŠΡ€Π·ΠΎ Π΄Π° Π³ΠΎ ΠΈΠ·ΠΊΠ»ΡŽΡ‡ΠΈΠΌ, послС Π±ΡŠΡ€Π·ΠΎ Π΄Π° Π³ΠΎ Π²ΠΊΠ»ΡŽΡ‡ΠΈΠΌ!

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

UPDATE ...;

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

Π’ΡƒΠΊ ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π²Π΅Ρ‡Π΅ Π΅ ΠΏΠΎ-Π΄ΠΎΠ±Ρ€ΠΎ, Ρ‡Π°ΠΊΠ°Π½Π΅Ρ‚ΠΎ Π΅ ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎ-ΠΌΠ°Π»ΠΊΠΎ. Но само Π΄Π²Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠ° развалят цялата красота:

  • ALTER TABLE сам ΠΈΠ·Ρ‡Π°ΠΊΠ²Π° всички Π΄Ρ€ΡƒΠ³ΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ Π½Π° масата, Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚Π΅Π»Π½ΠΎ Π΄ΡŠΠ»Π³ΠΈΡ‚Π΅ SELECT
  • Π”ΠΎΠΊΠ°Ρ‚ΠΎ ΡΠΏΡƒΡΡŠΠΊΡŠΡ‚ Π΅ ΠΈΠ·ΠΊΠ»ΡŽΡ‡Π΅Π½, "ΠΏΡ€Π΅Π»ΠΈΡ‚Π°" всяка промяна Π² Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π°, Π΄ΠΎΡ€ΠΈ ΠΈ Π½Π°ΡˆΠ°Ρ‚Π°. И няма Π΄Π° Π²Π»Π΅Π·Π΅ Π² Π°Π³Ρ€Π΅Π³Π°Ρ‚ΠΈΡ‚Π΅, Π²ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‡Π΅ трябва. нСприятности!

Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½Π° сСсийни ΠΏΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ

И Ρ‚Π°ΠΊΠ°, Π² ΠΏΡ€Π΅Π΄ΠΈΡˆΠ½Π°Ρ‚Π° вСрсия сС Π½Π°Ρ‚ΡŠΠΊΠ½Π°Ρ…ΠΌΠ΅ Π½Π° Ρ„ΡƒΠ½Π΄Π°ΠΌΠ΅Π½Ρ‚Π°Π»Π½Π° Ρ‚ΠΎΡ‡ΠΊΠ° - трябва ΠΏΠΎ някакъв Π½Π°Ρ‡ΠΈΠ½ Π΄Π° Π½Π°ΡƒΡ‡ΠΈΠΌ Ρ‚Ρ€ΠΈΠ³Π΅Ρ€Π° Π΄Π° Ρ€Π°Π·Π»ΠΈΡ‡Π°Π²Π° β€žΠ½Π°ΡˆΠΈΡ‚Π΅β€œ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈ Π² Ρ‚Π°Π±Π»ΠΈΡ†Π°Ρ‚Π° ΠΎΡ‚ β€žΠ½Π΅ Π½Π°ΡˆΠΈΡ‚Π΅β€œ. β€žΠΠ°ΡˆΠΈΡ‚Π΅β€œ сС пропускат Ρ‚Π°ΠΊΠ°, ΠΊΠ°ΠΊΡ‚ΠΎ са, Π½ΠΎ Π½Π° β€žΠ½Π΅ Π½Π°ΡˆΠΈΡ‚Π΅β€œ сС задСйстват. Π—Π° Ρ‚ΠΎΠ²Π° ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ сСсийни ΠΏΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²ΠΈ.

сСсия_рСпликация_роля

ΠŸΡ€ΠΎΡ‡Π΅Ρ‚ΠΈ Π½Π°Ρ€ΡŠΡ‡Π½ΠΈΠΊ:

ЗадСйстващият ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΡŠΠΌ ΡΡŠΡ‰ΠΎ сС влияС ΠΎΡ‚ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΎΠ½Π½Π°Ρ‚Π° ΠΏΡ€ΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π° сСсия_рСпликация_роля. Активирани Π±Π΅Π· Π΄ΠΎΠΏΡŠΠ»Π½ΠΈΡ‚Π΅Π»Π½ΠΈ инструкции (ΠΏΠΎ ΠΏΠΎΠ΄Ρ€Π°Π·Π±ΠΈΡ€Π°Π½Π΅), Ρ‚Ρ€ΠΈΠ³Π΅Ρ€ΠΈΡ‚Π΅ Ρ‰Π΅ сС задСйстват, ΠΊΠΎΠ³Π°Ρ‚ΠΎ ролята Π½Π° рСпликация Π΅ "origin" (ΠΏΠΎ ΠΏΠΎΠ΄Ρ€Π°Π·Π±ΠΈΡ€Π°Π½Π΅) ΠΈΠ»ΠΈ "local". ЗадСйствания, Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€Π°Π½ΠΈ Ρ‡Ρ€Π΅Π· посочванС ENABLE 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 = ''; -- Π²Π΅Ρ€Π½ΡƒΠ»ΠΈ Π² исходноС состояниС

Π—Π½Π°Π΅Ρ‚Π΅ Π»ΠΈ Π΄Ρ€ΡƒΠ³ΠΈ Π½Π°Ρ‡ΠΈΠ½ΠΈ? Π‘ΠΏΠΎΠ΄Π΅Π»Π΅Ρ‚Π΅ Π² ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΡ‚Π΅.

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

ДобавянС Π½Π° Π½ΠΎΠ² ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€