我试图通过检查表的文件修改日期(如本答案所述)来获取修改表的时间。但是结果并不总是正确的。更新表后,文件修改日期将在几分钟后更新。这是正确的行为吗?PostgreSQL是否将表修改存储在某些缓存中,然后将其刷新到硬盘上?
因此,如何获得表的正确上次修改日期(假设自动真空修改也可以)?
我在Linux Centos 6.2 x64下使用PostgreSQL 9.2。
我试图通过检查表的文件修改日期(如本答案所述)来获取修改表的时间。但是结果并不总是正确的。更新表后,文件修改日期将在几分钟后更新。这是正确的行为吗?PostgreSQL是否将表修改存储在某些缓存中,然后将其刷新到硬盘上?
因此,如何获得表的正确上次修改日期(假设自动真空修改也可以)?
我在Linux Centos 6.2 x64下使用PostgreSQL 9.2。
Answers:
没有可靠,权威的表上次修改时间记录。出于很多原因,使用relfilenode是错误的:
写操作最初会记录到写头日志(WAL),然后懒惰地记录到堆(表文件)。一旦记录进入WAL,Pg就不会急于将其写入堆中,甚至可能要等到下一个系统检查点才写入。
较大的表有多个分叉,您必须检查所有分叉并选择最新的时间戳。
SELECT
由于提示位的设置,一个简单的可以生成对基础表的写活动;
不会更改用户可见数据的自动清理和其他维护仍然会修改关系文件;
某些操作(如vaccum full
)将替换relfilenode。如果您尝试同时查看它而没有采取适当的锁定,则可能不是您期望的。
如果你不需要的可靠性,您可以潜在地使用中的信息pg_stat_database
和pg_stat_all_tables
。这些可以为您提供上次重置统计信息的时间,以及自上次重置统计信息以来的活动统计信息。它不会告诉您最近的活动是何时发生的,只是告诉您上次重置统计信息以来的活动,并且没有关于重置统计信息之前发生的情况的信息。因此它是有限的,但已经存在。
可靠地执行此操作的一个选项是使用触发器来更新包含每个表的最后修改时间的表。请注意,这样做会序列化对表的所有写入操作,从而破坏并发性。这也将增加每笔交易的开销。我不推荐。
一个不太麻烦的替代方法是使用LISTEN
和NOTIFY
。让外部守护进程连接到PostgreSQL并LISTEN
进行事件。当表更改时,使用ON INSERT OR UPDATE OR DELETE
触发器发送NOTIFY
s,表oid作为通知有效负载。这些在事务提交时发送。您的守护程序可以累积更改通知,并将它们懒惰地写回到数据库中的表中。如果系统崩溃,则会丢失最近修改的记录,但是没关系,如果崩溃后要启动,则将所有表都视为刚刚修改。
为了避免出现最严重的并发问题,您可以改为使用before insert or update or delete or truncate on tablename for each statement execute
触发器记录更改时间戳,该触发器一般采用关系oid作为参数。这将(relation_oid, timestamp)
在更改记录表中插入一对。然后,您可以在单独的连接上创建一个帮助程序进程,或者由您的应用定期调用该进程,然后将该表汇总为最新信息,将其合并为最新更改的摘要表,然后截断日志表。相对于侦听/通知方法,此方法的唯一优点是它不会在崩溃时丢失信息-但效率甚至更低。
另一种方法可能是写一个C扩展功能使用(例如)ProcessUtility_hook
,ExecutorRun_hook
等以捕获表更改和懒惰地更新统计信息。我没有看到这有多实用。查看源代码中的各种_hook选项。
最好的方法是修补统计代码以记录此信息,然后将修补程序提交给PostgreSQL以包含在核心中。不要仅仅从编写代码开始;在您对-黑客进行了充分的思考后,便提出了一个明确的定义(例如,从阅读代码开始,不要仅仅问“我如何...”)。将上次更新的时间添加到中可能会很好pg_stat_...
,但是您必须说服社区这是值得的开销,或者提供一种使其可以有选择地进行跟踪的方法-并且您必须编写代码来保留统计信息和提交补丁,因为只有想使用此功能的人会为此而烦恼。
如果我必须这样做,而又没有时间编写补丁来正确地执行此操作,则可能会使用上面概述的“监听/通知”方法。
更新:PostgreSQL 9.5有提交时间戳。如果启用了它们postgresql.conf
(并且过去也启用了),则可以检查具有最大行的提交时间戳,xmin
以近似上次修改的时间。这只是一个近似值,因为如果删除了最近的行,则不会将它们计算在内。
另外,提交时间戳记记录仅保留有限的时间。因此,如果您想知道何时修改的表很少,那么答案将是“不久前不知道”。
PostgreSQL 9.5让我们跟踪最后修改的提交。
使用以下查询检查是否启用轨道提交
show track_commit_timestamp;
如果返回“ ON”,则转到第3步,否则修改postgresql.conf
cd /etc/postgresql/9.5/main/
vi postgresql.conf
更改
track_commit_timestamp = off
至
track_commit_timestamp = on
重新启动系统
重复步骤1。
使用以下查询跟踪上一次提交
SELECT pg_xact_commit_timestamp(xmin), * FROM YOUR_TABLE_NAME;
SELECT pg_xact_commit_timestamp(xmin), * FROM YOUR_TABLE_NAME where COLUMN_NAME=VALUE;
sudo service postgresql restart
。
是的,这是可以预期的行为-有关更改的数据会立即存储到事务日志中。可以使用checkpoint_timeout延迟(默认值为5分钟)来更新数据文件。您要求的任何时间,Postgres都不会永久保留。
为了维护客户端应用程序上某些表的缓存,我几乎有相同的要求。我几乎说了,因为我真的不需要知道上一次修改的时间,而只是要检测自上次同步缓存以来是否发生了更改。
这是我的方法:
如果每个表上都有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
,并且该标记对于表中数据的每个版本都是唯一的。