postgresql 在Postgres函数的情况下事务如何工作

9fkzdhlc  于 2022-12-23  发布在  PostgreSQL
关注(0)|答案(1)|浏览(164)

我试图理解事务在Postgres中是如何工作的,以及当多个命令试图在同一个表上工作时会发生什么,我的怀疑与我进行的一个小实验有关。
考虑一个名为experiment的表,该表上有一个触发器(experiment_log),该触发器在每次更新、删除或插入之后触发。
现在考虑这个函数。

CREATE OR REPLACE FUNCTION test_func() RETURNS void AS $body$
DECLARE
  _q_txt text;
  version_var integer;
BEGIN
    EXECUTE 'DROP TRIGGER IF EXISTS experiment_log ON experiment';
    
    SELECT version INTO version_var FROM experiment;
    RAISE NOTICE 'VERSION AFTER DROPPING TRIGGER: %', version_var;
    
    EXECUTE 'SELECT pg_sleep(20);';
    
    SELECT version INTO version_var FROM experiment;
    RAISE NOTICE 'VERSION BEFORE RECREATING TRIGGER: %', version_var;
    
    EXECUTE 'CREATE TRIGGER experiment_log AFTER INSERT OR UPDATE OR DELETE ON experiment FOR EACH ROW EXECUTE PROCEDURE experiment_log_trigger_func();';

END;
$body$
language 'plpgsql';

因此,这个函数丢弃触发器,并在重新创建这个触发器之前等待20秒。现在,在函数休眠期间执行的任何更新操作都会阻塞。这意味着,在函数test_func执行完毕之前,我无法更新experiment表。
有人能解释这种行为吗?似乎我错过了一些东西来解释这种行为。

cbeh67ev

cbeh67ev1#

这是因为DROP TRIGGER在表上放置了一个ACCESS EXCLUSIVE锁,并且该锁一直保持到事务结束,也就是说,在函数调用的整个持续时间内。
如果要临时禁用触发器,请使用

ALTER TABLE experiment DISABLE TRIGGER experiment_log;

我想从文档中给予你一个参考,但是DROP TRIGGER的锁级别没有文档,但是有文档说明SQL语句获取锁:
此外,大多数PostgreSQL命令会自动获取适当模式的锁,以确保在执行命令时不会以不兼容的方式删除或修改引用的表。
在那里你还可以找到锁被持有的时间:
一旦获得锁,锁通常会一直保持到事务结束。
要查找DROP TRIGGER占用的锁,请尝试以下简单实验:

CREATE TABLE t();

CREATE TRIGGER whatever BEFORE UPDATE ON t
   FOR EACH ROW EXECUTE FUNCTION suppress_redundant_updates_trigger();

BEGIN;  -- start a transaction

DROP TRIGGER whatever ON t;

SELECT mode FROM pg_locks
WHERE pid = pg_backend_pid()    -- only locks for the current session
  AND relation = 't'::regclass; -- only locks on "t"

        mode         
═════════════════════
 AccessShareLock
 AccessExclusiveLock
(2 rows)

COMMIT;

您可以看到表上有一个ACCESS SHARE锁和一个ACCESS EXCLUSIVE锁。

相关问题