PostgreSQL反模式:绕过触发器更改数据

迟早,许多人都需要大规模修复表条目中的某些内容。我已经告诉过如何做得更好,以及如何做-最好不要做。今天,我将讨论大规模更新触发触发器的第二个方面

例如,在需要修复某些东西的桌子上,一个邪恶的触发器挂起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配置变量的影响当复制角色为“原始”(默认)或“本地”时,将触发没有其他指令(默认)的触发器。指示启用的触发器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 = ''; --    

你知道其他方式吗?分享评论。

All Articles