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

迟早,许多人都需要大量修复表记录中的某些内容。 我已经 告诉我如何做得更好,以及如何 - 最好不要这样做。 今天要说的是海量更新的第二个方面—— 关于触发器.

例如,在您需要修复某些东西的桌子上,一个邪恶的触发器挂起 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
  • 当触发器关闭时 “飞过”任何变化 在桌子上,甚至不是我们的。 它不会进入聚合,尽管它应该。 麻烦!

管理会话变量

因此,在之前的版本中,我们偶然发现了一个基本点——我们需要以某种方式教会触发器区分表中“我们的”更改和“不是我们的”更改。 “我们的”按原样被跳过,但在“不是我们的”时它们被触发。 为此,您可以使用 会话变量.

会话复制角色

我们读了 手动的:

触发机制也受配置变量的影响 会话复制角色. 在没有额外说明的情况下启用(默认),当复制角色是“源”(默认)或“本地”时,触发器将触发。 通过指定启用触发器 ENABLE REPLICA, 只有当 当前会话模式 - “副本”,并通过指定启用触发器 ENABLE ALWAYS, 无论当前的复制模式如何,都将起作用。

我要特别强调的是,该设置不适用于 all-all at once,因为 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

添加评论