Postgres 13.4
我已经设置了一些pg_cron作业,以便定期从类似日志的文件中删除旧记录。我想做的是在执行清除之后运行VACUUM ANALYZE。不幸的是,我无法解决如何在存储函数中这样做。我错过了一个诡计吗?存储过程是否更合适?
举个例子,下面是我的一个清除例程
CREATE OR REPLACE FUNCTION dba.purge_event_log (
    retain_days_in integer_positive default 14)
RETURNS int4
AS $BODY$
WITH  -- Use a CTE so that we've got a way of returning the count easily.
deleted AS (
-- Normal-looking code for this requires a literal:
-- where your_dts < now() - INTERVAL '14 days'
-- Don't want to use a literal, SQL injection, etc.
-- Instead, using a interval constructor to achieve the same result:
   DELETE
     FROM dba.event_log
    WHERE dts < now() - make_interval (days => $1)
RETURNING *
),
----------------------------------------
-- Save details to a custom log table
----------------------------------------
logit AS (
insert into dba.event_log (name, details)
    values ('purge_event_log(' || retain_days_in::text || ')',
             'count = ' || (select count(*)::text from deleted)
           )
)
----------------------------------------
-- Return result count
----------------------------------------
select count(*) from deleted;
$BODY$
  LANGUAGE sql;
COMMENT ON FUNCTION dba.purge_event_log (integer_positive) IS
'Delete dba.event_log records older than the day count passed in, with a default retention period of 14 days.';事实是,在这种情况下,我并不真正关心这个例程的count(*)结果。但我可能希望在其他类似的情况下得到一个结果和一个额外的行动。如您所见,该例程删除记录,使用CTE将报表insert到另一个表中,然后返回结果。无论如何,我认为这个示例是一个很好的方法,可以让我了解存储函数中的替代方案和选项。我在这里想要实现的主要目标是删除记录,然后运行维护。如果这不适合存储函数或过程,我可以用表名为vacuum_list表写出一个条目,并在该列表中运行另一个作业。
如果有更明智的方法来接近vacuum而不需要额外的,我当然会对此感兴趣。但是,我也有兴趣了解在PL/PgSQL例程中可以组合哪些操作a的限制。
帕维尔·斯特胡勒的回答是正确和完整的。我决定在这里进行一些跟踪,因为我喜欢深入了解代码中的bug、Postgres中的行为等等,以便更好地了解我正在处理的问题。我在下面包括一些笔记,供任何发现有用的人使用。
命令不能执行..。
对“真空不能在事务块内执行”的引用为我提供了一种更好的方法来搜索文档中类似的受限命令。下面的信息可能并不涵盖所有内容,但这是一个开始。
Command                Limitation
CREATE DATABASE
ALTER DATABASE         If creating a new table space.
DROP DATABASE
CLUSTER                Without any parameters.
CREATE TABLESPACE
DROP TABLESPACE
REINDEX                All in system catalogs, database, or schema.
CREATE SUBSCRIPTION    When creating a replication slot (the default behavior.)
ALTER SUBSCRIPTION     With refresh option as true.
DROP SUBSCRIPTION      If the subscription is associated with a replication slot.
COMMIT PREPARED
ROLLBACK PREPARED
DISCARD ALL
VACUUM接受的答案表明,该限制与所使用的特定服务器端语言无关。我刚遇到一个旧线程,它对存储的函数和事务有一些很好的解释和链接:
Do stored procedures run in database transaction in Postgres?
样本码
我还想知道存储过程,因为它们可以控制事务。我在PG 13中试用了它们,不,代码被当作存储函数来处理,直到错误消息。
对于任何从事这类工作的人,下面是sQL和PL/PgSQL存储函数和过程的"hello world“示例,以测试VACCUM在这些情况下的行为。剧透者:它不起作用,正如广告所示。
SQL函数
/*
select * from dba.vacuum_sql_function();
Fails:
ERROR:  VACUUM cannot be executed from a function
CONTEXT:  SQL function "vacuum_sql_function" statement 1. 0.000 seconds. (Line 13).
*/
DROP FUNCTION IF EXISTS dba.vacuum_sql_function();
CREATE FUNCTION dba.vacuum_sql_function()
RETURNS VOID
LANGUAGE sql
AS $sql_code$
VACUUM ANALYZE activity;
$sql_code$;
select * from dba.vacuum_sql_function(); -- Fails.PL/PgSQL函数
/*
select * from dba.vacuum_plpgsql_function();
Fails:
ERROR:  VACUUM cannot be executed from a function
CONTEXT:  SQL statement "VACUUM ANALYZE activity"
PL/pgSQL function vacuum_plpgsql_function() line 4 at SQL statement. 0.000 seconds. (Line 22).
*/
DROP FUNCTION IF EXISTS dba.vacuum_plpgsql_function();
CREATE FUNCTION dba.vacuum_plpgsql_function()
RETURNS VOID
LANGUAGE plpgsql
AS $plpgsql_code$
BEGIN
VACUUM ANALYZE activity;
END
$plpgsql_code$;
select * from dba.vacuum_plpgsql_function();SQL过程
/*
call dba.vacuum_sql_procedure();
ERROR:  VACUUM cannot be executed from a function
CONTEXT:  SQL function "vacuum_sql_procedure" statement 1. 0.000 seconds. (Line 20).
*/
DROP PROCEDURE IF EXISTS dba.vacuum_sql_procedure();
CREATE PROCEDURE dba.vacuum_sql_procedure()
LANGUAGE SQL
AS $sql_code$
VACUUM ANALYZE activity;
$sql_code$;
call dba.vacuum_sql_procedure();PL/PgSQL过程
 /*
call dba.vacuum_plpgsql_procedure();
ERROR:  VACUUM cannot be executed from a function
CONTEXT:  SQL statement "VACUUM ANALYZE activity"
PL/pgSQL function vacuum_plpgsql_procedure() line 4 at SQL statement. 0.000 seconds. (Line 23).
*/
DROP PROCEDURE IF EXISTS dba.vacuum_plpgsql_procedure();
CREATE PROCEDURE dba.vacuum_plpgsql_procedure()
LANGUAGE plpgsql
AS $plpgsql_code$
BEGIN
VACUUM ANALYZE activity;
END
$plpgsql_code$;
call dba.vacuum_plpgsql_procedure();其他选项
很多。据我所知,在Postgres中运行的服务器端代码中不支持VACUUM和其他一些命令。因此,您的代码需要从其他地方开始。这可以是:
在您的服务器操作系统中有什么like.
pg_cron.
当我们部署在RDS上时,我将查看最后两个选项。还有一个:
AUTOVACCUM和偶尔的VACCUM做他们的事情。--这是相当容易做到的,似乎可以很好地满足我们的大部分需求。
另一个想法
如果您确实想要更多的控制和一些自定义日志记录,那么我想象的是这样一个表:
CREATE TABLE IF NOT EXISTS dba.vacuum_list (
    database_name   text,
    schema_name     text,
    table_name      text,
    run             boolean,
    run_analyze     boolean,
    run_full        boolean,
    last_run_dts    timestamp)
ALTER TABLE dba.vacuum_list ADD CONSTRAINT
   vacuum_list_pk
   PRIMARY KEY (database_name, schema_name, table_name);那只是个素描。想法是这样的:
当一张桌子需要吸尘时,
INSERT as vacuum_list,至少就您而言是这样。在我的例子中,
UPSERT,因为我不需要一个完整的类似日志的表,每个表只需要一行,最后的结果和/或挂起的状态。VACUUM。。
最后一个选择是我感兴趣的。我们的VACUUM呼叫都没有一个工作相当长的时间,因为有一个月的死亡连接从某个重要的方面。VACUUM看起来运行得很好,在这种情况下,它不能删除很多行。(因为超级老的“打开”事务ID、可见性映射等等)看到这类事情的唯一方法似乎是VACUUM VERBOSE和研究输出。或者,为了记录真空时间,更重要的是,关系的大小变化到似乎什么事情都没有发生的情况,当它似乎应该发生的时候。
发布于 2021-12-01 04:52:57
VACUUM是“顶级”命令。它不能从PL/pgSQL执行,也不能从任何其他PL执行。
https://stackoverflow.com/questions/70170477
复制相似问题