获取PostgreSQL数据库表的最后修改日期


35

我试图通过检查表的文件修改日期(如本答案所述)来获取修改表的时间。但是结果并不总是正确的。更新表后,文件修改日期将在几分钟后更新。这是正确的行为吗?PostgreSQL是否将表修改存储在某些缓存中,然后将其刷新到硬盘上?

因此,如何获得表的正确上次修改日期(假设自动真空修改也可以)?

我在Linux Centos 6.2 x64下使用PostgreSQL 9.2。


4
我认为文件修改时间不可靠。它也可能由于自动真空而改变。唯一可靠的方法是将修改时间戳记存储在表中,并由触发器维护。
a_horse_with_no_name 2014年

一种想法是,将WAL文件中存储的信息提交事务后一段时间(或更短或更长)写入数据文件。如果需要,可以将其称为缓存:)否则,我将@a_horse_with_no_name的内容放在第二位。
dezso 2014年

Answers:


35

没有可靠,权威的表上次修改时间记录。出于很多原因,使用relfilenode是错误的:

  • 写操作最初会记录到写头日志(WAL),然后懒惰地记录到堆(表文件)。一旦记录进入WAL,Pg就不会急于将其写入堆中,甚至可能要等到下一个系统检查点才写入。

  • 较大的表有多个分叉,您必须检查所有分叉并选择最新的时间戳。

  • SELECT由于提示位的设置,一个简单的可以生成对基础表的写活动;

  • 不会更改用户可见数据的自动清理和其他维护仍然会修改关系文件;

  • 某些操作(如vaccum full)将替换relfilenode。如果您尝试同时查看它而没有采取适当的锁定,则可能不是您期望的。

一些选择

如果你不需要的可靠性,您可以潜在地使用中的信息pg_stat_databasepg_stat_all_tables。这些可以为您提供上次重置统计信息的时间,以及上次重置统计信息以来的活动统计信息。它不会告诉您最近的活动是何时发生的,只是告诉您上次重置统计信息以来的活动,并且没有关于重置统计信息之前发生的情况的信息。因此它是有限的,但已经存在。

可靠地执行此操作的一个选项是使用触发器来更新包含每个表的最后修改时间的表。请注意,这样做会序列化对表的所有写入操作,从而破坏并发性。这也将增加每笔交易的开销。我不推荐。

一个不太麻烦的替代方法是使用LISTENNOTIFY。让外部守护进程连接到PostgreSQL并LISTEN进行事件。当表更改时,使用ON INSERT OR UPDATE OR DELETE触发器发送NOTIFYs,表oid作为通知有效负载。这些在事务提交时发送。您的守护程序可以累积更改通知,并将它们懒惰地写回到数据库中的表中。如果系统崩溃,则会丢失最近修改的记录,但是没关系,如果崩溃后要启动,则将所有表都视为刚刚修改。

为了避免出现最严重的并发问题,您可以改为使用before insert or update or delete or truncate on tablename for each statement execute触发器记录更改时间戳,该触发器一般采用关系oid作为参数。这将(relation_oid, timestamp)在更改记录表中插入一对。然后,您可以在单独的连接上创建一个帮助程序进程,或者由您的应用定期调用该进程,然后将该表汇总为最新信息,将其合并为最新更改的摘要表,然后截断日志表。相对于侦听/通知方法,此方法的唯一优点是它不会在崩溃时丢失信息-但效率甚至更低。

另一种方法可能是写一个C扩展功能使用(例如)ProcessUtility_hookExecutorRun_hook等以捕获表更改和懒惰地更新统计信息。我没有看到这有多实用。查看源代码中的各种_hook选项。

最好的方法是修补统计代码以记录此信息,然后将修补程序提交给PostgreSQL以包含在核心中。不要仅仅从编写代码开始;在您对-黑客进行了充分的思考后,便提出了一个明确的定义(例如,从阅读代码开始,不要仅仅问“我如何...”)。将上次更新的时间添加到中可能会很好pg_stat_...,但是您必须说服社区这是值得的开销,或者提供一种使其可以有选择地进行跟踪的方法-并且您必须编写代码来保留统计信息和提交补丁,因为只有想使用此功能的人会为此而烦恼。

我会怎么做

如果我必须这样做,而又没有时间编写补丁来正确地执行此操作,则可能会使用上面概述的“监听/通知”方法。

PostgreSQL 9.5提交时间戳的更新

更新:PostgreSQL 9.5有提交时间戳。如果启用了它们postgresql.conf(并且过去也启用了),则可以检查具有最大行的提交时间戳,xmin近似上次修改的时间。这只是一个近似值,因为如果删除了最近的行,则不会将它们计算在内。

另外,提交时间戳记记录仅保留有限的时间。因此,如果您想知道何时修改的表很少,那么答案将是“不久前不知道”。


17

PostgreSQL 9.5让我们跟踪最后修改的提交。

  1. 使用以下查询检查是否启用轨道提交

    show track_commit_timestamp;
  2. 如果返回“ ON”,则转到第3步,否则修改postgresql.conf

    cd /etc/postgresql/9.5/main/
    vi postgresql.conf
    

    更改

    track_commit_timestamp = off

    track_commit_timestamp = on

    重新启动系统

    重复步骤1。

  3. 使用以下查询跟踪上一次提交

    SELECT pg_xact_commit_timestamp(xmin), * FROM  YOUR_TABLE_NAME;
    
    SELECT pg_xact_commit_timestamp(xmin), * FROM YOUR_TABLE_NAME where COLUMN_NAME=VALUE;
    

1
您不必在步骤2中重新启动系统。只需重新启动该过程即可。例如sudo service postgresql restart
ijoseph

3

是的,这是可以预期的行为-有关更改的数据会立即存储到事务日志中。可以使用checkpoint_timeout延迟(默认值为5分钟)来更新数据文件。您要求的任何时间,Postgres都不会永久保留。


我不确定我是否理解这个问题的答案。是的,数据已存储到事务日志中,但这并不意味着一个人可以轻松获取特定表的修改时间(如果该内容仍在日志中,则可以解析该日志,但是可以重播一些内容)很快)。
查尔斯·达菲

当然,您可以从日志中获取所有必要的信息,但是问题是针对数据文件的mtime的-提交后,数据文件的实现可能是非常随机的-几秒钟-几分钟(最多1小时)。
Pavel Stehule

OP自己的尝试是通过查看文件来完成的,但是他们的真正意图显然是要获得一个表mtime。但是,是的,我知道您现在从这里来(解释为什么他们在做什么不起作用)。
查尔斯·达菲

2

为了维护客户端应用程序上某些表的缓存,我几乎有相同的要求。我几乎,因为我真的不需要知道上一次修改的时间,而只是要检测自上次同步缓存以来是否发生了更改。

这是我的方法:

如果每个表上都有id(PK),created_on(插入时间戳记)和updated_on(更新时间戳记,可能为NULL)列,则可以

SELECT id,greatest(created_on,updated_on) FROM %s ORDER BY greatest(created_on,updated_on) DESC LIMIT 1;

如果您对此进行合并并添加了行数,则可以构建一个看起来像的版本标记count:id#timestamp,并且该标记对于表中数据的每个版本都是唯一的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.