postgresql 检查实体化视图的上次刷新时间

mrwjdhj3  于 2022-11-04  发布在  PostgreSQL
关注(0)|答案(6)|浏览(273)

我有一个名为price_changes的物化视图,用于一些报告。我还有一个cron作业,用于刷新refresh materialized view price_changes的物化视图。一切都运行得很好。
我想给予查看报告的用户一条消息“Data is fresh as of X”。我可以在cron运行时将其存储在某个地方,但是postgres是否已经将此元数据存储在某个地方了呢?

fhg3lkii

fhg3lkii1#

我认为9.3.4版的系统中没有内置任何内容提供此功能。当我需要提供上次刷新的日期时,我在实体化视图的选择查询中添加了一个名为'last_refresh'的列,因为实体化视图中的数据在刷新之前不会更改。
出于安全原因,我也更喜欢这样做,因为如果信息存储在系统表中,您可能不想给予sql用户对系统表的访问权限。
根据您是否需要时间,您可以使用以下任一方法:

  1. now()
  2. now()
    仅限日期:
CREATE MATERIALIZED VIEW mv_address AS 
SELECT *, CURRENT_DATE AS last_refresh FROM address;

包含日期和时间:

CREATE MATERIALIZED VIEW mv_address AS 
SELECT *, now() AS last_refresh FROM address;
  • 更新日期:2017年2月17日 *

PostgreSQL版本9.4+现在包括CONCURRENTLY选项。如果你使用REFRESH MATERIALIZED VIEW CONCURRENTLY选项,请注意@Smudge在注解中所指示的内容。这实际上只会对大型和频繁更新的数据集造成问题。如果你的数据集很小或很少更新,那么你应该没问题。

xmd2e60i

xmd2e60i2#

由于具体化视观表是储存在磁盘上的数据区段,因此它们在文件系统中会有一个Map的档案。当您呼叫REFRESH MATERIALIZED VIEW时,磁盘上的数据会以新的文件名称重新建立。因此,您可以在pg_class中交叉参照relfilenode,以查询视观表的修改/建立时戳:

[user@server /]# psql -c "create materialized view myview as select aid from pgbench_accounts where aid < 100"
SELECT 99
[user@server /]# psql -c "select relfilenode from pg_class where relname = 'myview'"
 relfilenode 
-------------
       16445
(1 row)

[user@server /]# ls -l /var/lib/edb/as12/data/base/15369/16445
-rw------- 1 enterprisedb enterprisedb 8192 Jun 14 23:28 /var/lib/edb/as12/data/base/15369/16445
[user@server /]# date
Mon Jun 14 23:29:16 UTC 2021
[user@server /]# psql -c "refresh materialized view myview"
REFRESH MATERIALIZED VIEW
[user@server /]# psql -c "select relfilenode from pg_class where relname = 'myview'"
 relfilenode 
-------------
       16449
(1 row)

[user@server /]# ls -l /var/lib/edb/as12/data/base/15369/16449
-rw------- 1 enterprisedb enterprisedb 8192 Jun 14 23:29 /var/lib/edb/as12/data/base/15369/16449
[user@server /]#
3phpmpom

3phpmpom3#

WITH
        pgdata AS (
                SELECT
                        setting AS path
                FROM
                        pg_settings
                WHERE
                        name = 'data_directory'
        ),
        path AS (
                SELECT
                        CASE
                                WHEN pgdata.separator = '/' THEN '/'    -- UNIX
                                ELSE '\'                                -- WINDOWS
                        END AS separator
                FROM 
                        (SELECT SUBSTR(path, 1, 1) AS separator FROM pgdata) AS pgdata
        )
SELECT
        ns.nspname||'.'||c.relname AS mview,
        (pg_stat_file(pgdata.path||path.separator||pg_relation_filepath(ns.nspname||'.'||c.relname))).modification AS refresh
FROM
        pgdata,
        path,
        pg_class c
JOIN
        pg_namespace ns ON c.relnamespace=ns.oid
WHERE
        c.relkind='m'
;
e1xvtsh3

e1xvtsh34#

我认为@richyen解决方案是最好的,如果你能访问服务器。
如果你不这样做,你必须采取手动存储上次刷新日期作为元数据的方式。

  • 正如@thames的响应中的列一样,主要缺点是列的额外存储空间(重复次数与具有MV的行一样多)和@smudge所述的大表中的刷新问题。
  • 在自定义表中.主要的缺点是你必须确保所有的刷新都包括表的更新。
-- Create table
CREATE TABLE pg_matviews_last_refreshed (
  matviewowner NAME NOT NULL,
  matviewname NAME NOT NULL,
  schemaname NAME NOT NULL,
  last_refreshed TIMESTAMP NOT NULL,
  CONSTRAINT pk UNIQUE (matviewowner, matviewname, schemaname)
)

CREATE VIEW pg_matviews_extra AS
SELECT * 
FROM pg_matviews
NATURAL JOIN pg_matviews_last_refreshed

-- Set initial values
INSERT INTO pg_matviews_last_refreshed (
  matviewowner,
  matviewname,
  schemaname,
  last_refreshed
)
SELECT 
  matviewowner,
  matviewname,
  schemaname,
  CURRENT_TIMESTAMP
FROM pg_matviews;

-- Consult dates
SELECT * FROM pg_matviews_extra

-- Refresh 
BEGIN;
REFRESH MATERIALIZED VIEW CONCURRENTLY my_materialized_view;

-- Insert update date into last_refreshed table. Of course, if 
-- more complex permissions are into place, the query becomes more
-- complex to discover owner, schema, etc.
INSERT INTO pg_matviews_last_refreshed (matviewname, matviewowner, schemaname, last_refreshed)
SELECT matviewname, matviewowner, schemaname, CURRENT_TIMESTAMP
FROM pg_matviews
WHERE matviewname='my_materialized_view'
ON CONFLICT (matviewname, matviewowner, schemaname)
DO UPDATE SET last_refreshed=EXCLUDED.last_refreshed;
COMMIT;
  • 与使用专用表存储上次刷新日期类似,您可以将该信息隐式存储在MV注解中:
-- Refresh operation
BEGIN;
REFRESH MATERIALIZED VIEW CONCURRENTLY my_materialized_view;
-- This statement can be wrapped into a function to allow using CURRENT_TIMESTAMP
COMMENT ON MATERIALIZED VIEW my_materialized_view IS '{"last_refreshed": "2021-07-07T09:30:59Z"}'
COMMIT;

-- Retrieve refresh date
SELECT (pg_catalog.obj_description('my_materialized_view'::regclass, 'pg_class')::json->>'last_refreshed')::timestamp;
kx1ctssn

kx1ctssn5#

感谢您@ajaest的帮助。我们的生产环境有许多示例。我们必须防止实体化竞争,刷新速度不能超过50秒。这是我们的解决方案。我们使用了对实体化视图的评论

-- dodanie funkcji do odświeżania widoku
CREATE OR REPLACE FUNCTION public.refresh_mv_users()
    RETURNS timestamp AS $$
DECLARE
    last_refreshed  TIMESTAMP;
    next_refresh    TIMESTAMP;
    comment_sql     text;
BEGIN

    SELECT ((pg_catalog.obj_description('public.mv_users'::regclass, 'pg_class')::json->>'last_refreshed')::timestamp) into last_refreshed;
    SELECT (last_refreshed + (50 * interval '1 seconds')) into next_refresh;

    IF next_refresh < now() THEN
        REFRESH MATERIALIZED VIEW CONCURRENTLY public.mv_users;
        comment_sql := 'COMMENT ON MATERIALIZED VIEW public.mv_users is '|| quote_literal('{"last_refreshed": "' || now() || '"}');
        execute comment_sql;
        SELECT now() into last_refreshed;
    END IF;

    RETURN last_refreshed;
END;
$$  LANGUAGE plpgsql;
pgvzfuti

pgvzfuti6#

从CLI

让我们首先为要检查的示例化视图及其所属的数据库声明一些变量。

materialized_view_name='my_materialized_view'
database_name='my_database'

接下来,我们需要找到实体化视图的id,可以通过查询pg_class表来完成。
在下面的示例化w查询中,将<materialized_view_name>替换为示例化视图的名称

id=$(psql -d $database_name -c "SELECT relfilenode FROM pg_class WHERE relname = '<materialized_view_name>';" -tA)

现在我们可以找到示例化视图的文件路径,从而找到它的最后更新时间。

file_path=$(find / -name "${id}" 2>/dev/null)
ls -l $file_path

额外的好处:有时服务器的时区可能与您的本地机器不同。您可以通过运行以下命令来获得对服务器时区的引用。

date

相关问题