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

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

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

评论

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

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

#1 楼

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



写入最初记录到写头日志(WAL),然后懒惰地记录到堆(表文件)。一旦记录在WAL中,Pg就不会急于将其写入堆中,甚至可能在下一个系统检查点之前都不会被写入;
较大的表具有多个fork,您必须检查所有分叉并选择最新的时间戳;
由于提示位设置,一个简单的SELECT可以生成对基础表的写活动;
不会改变用户可见数据的自动清理和其他维护仍会修改该关系文件;
某些操作(例如vaccum full)将替换relfilenode。如果您尝试同时查看它而没有采取适当的锁定,则可能不是您期望的。

一些选择

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

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

不太可怕的替代方法是使用LISTENNOTIFY。让外部守护程序进程连接到PostgreSQL和LISTEN以获得事件。当表更改时,使用ON INSERT OR UPDATE OR DELETE触发器发送NOTIFY,表oid作为通知有效负载。这些在事务提交时发送。您的守护程序可以累积更改通知,并将它们懒惰地写回到数据库中的表中。如果系统崩溃,则您将丢失最近修改的记录,但是没关系,如果崩溃后要启动,则将所有表都视为刚刚修改。并发问题,您可以改用before insert or update or delete or truncate on tablename for each statement execute触发器记录更改时间戳,该触发器一般采用关系oid作为参数。这会将一个(relation_oid, timestamp)对插入到更改日志表中。然后,您可以在单独的连接上创建一个帮助程序流程,或者由您的应用定期调用该助手流程,然后将该表汇总为最新信息,将其合并为最新更改的摘要表,然后截断日志表。相对于listen / notify方法,此方法的唯一优点是它不会在崩溃时丢失信息-但效率甚至更低。 (例如)ProcessUtility_hookExecutorRun_hook等,以捕获表更改并延迟更新统计信息。我没看过这有多实用。看看源代码中的各种_hook选项。

最好的方法是修补统计代码以记录此信息,然后将修补程序提交给PostgreSQL以包含在核心中。不要仅仅从编写代码开始;在您对-黑客进行了充分的思考后,便提出了一个明确的定义(例如,从阅读代码开始,而不是仅仅问“我如何...”)。将最后更新的时间添加到pg_stat_...可能会很好,但是您必须说服社区这是值得的开销,或者提供一种对其进行选择性跟踪的方法-并且您必须编写代码来保留统计信息并提交补丁程序,因为只有想使用此功能的人会对此感到烦恼。没有时间编写补丁来正确地执行它,我可能会使用上面概述的监听/通知方法。 :PostgreSQL 9.5有提交时间戳。如果您在postgresql.conf中启用了这些功能(并且过去也是如此),则可以检查xmin最大的行的提交时间戳,以近似上次修改的时间。这只是一个近似值,因为如果删除了最近的行,则不会对其进行计数。

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

#2 楼

PostgreSQL 9.5让我们跟踪上次修改的提交。



使用以下查询检查跟踪提交是打开还是关闭

show track_commit_timestamp;



如果返回“ ON”,请转到步骤3,否则修改postgresql.conf。





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


重新启动PostgreSQL服务器
重复步骤1。

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

track_commit_timestamp = off




评论


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

– ijoseph
18年6月7日,0:55

#3 楼

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

评论


我不确定我是否能回答这个问题。是的,数据已存储到事务日志中,但这并不意味着可以轻松获得对特定表的修改时间(如果该内容仍在日志中,则可以解析该日志,但是可以重放某些内容很快)。

–查尔斯·达菲(Charles Duffy)
16/09/14在15:26

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

– Pavel Stehule
2016年9月14日下午16:47

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

–查尔斯·达菲(Charles Duffy)
16年9月14日在16:49

#4 楼

为了维护客户端应用程序上某些表的缓存,我几乎有相同的要求。我几乎要说,因为我真的不需要知道上一次修改的时间,而只是要检测自上次同步缓存以来是否发生了某些更改。 />
假设每个表上都有一个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的版本标记,并且该标记对于表中数据的每个版本都是唯一的。