我要做的事情
我正在开发一个web服务,它在多个服务器实例中运行,所有实例都访问同一个RDBMS(PostgreSQL(。虽然数据库是持久性所必需的,但它包含的数据很少,这就是为什么每个服务器实例都有一个所有数据的缓存。此外,该应用程序非常简单,因为它只在相当简单的表中插入新行,并以预定的方式从所有服务器实例中选择数据(没有更新或更改……只有插入和读取(。
目前的实施方式
基本上我有一张大致如下的表格:
id BIGSERIAL,
creation_timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- further data columns...
服务器每隔几秒钟就会做这样的事情(伪代码(:
get all rows with creation_timestamp > lastMaxTimestamp
lastMaxTimestamp = max timestamp for all data just retrieved
insert new rows into application cache
我遇到的问题
应用程序在更新缓存时跳过某些行。我分析了这个问题,发现问题是由以下方式引起的:
- 一个服务器实例正在事务上下文中创建一个新行。从相关联的序列(id=n(中检索新行的id,并设置creation_timestamp(具有值ts_1(
- 另一个服务器在不同事务的上下文中执行相同的操作。该事务中的新行得到id=n+1和creation_timestamp ts_2(其中ts_1<ts_2(
- 事务2在事务1之前完成
- 其中一个服务器执行"select all rows with creation_timestamp>lastMaxTimestamp"。它得到第n+1行,但不是n1行。它将lastMaxTimestamp设置为ts_2
- 事务1完成
- 一段时间后,步骤4中的服务器再次执行"select all rows with creation_timestamp>lastMaxTimestamp"。但是由于lastMaxTimestamp=ts_2和ts_2>ts_1,因此永远不会在该服务器上读取行n
注意:CURRENT_TIMESTAMP在事务期间具有相同的值,即事务开始时间。
因此,应用程序将不一致的数据放入其缓存,并且无法根据插入时间戳或序列id获取新行。事务隔离级别并不会真正改变这种情况,因为问题本质上是由事务2在事务1之前完成造成的。
我的问题
我是不是错过了什么?我认为必须有一种简单的方法来获取RDBMS的所有新行,但我无法想出一个简单的解决方案。。。至少用一个一致的简单解决方案。由于性能原因,不能接受广泛的锁定(例如表(。简单地试图确保从该序列中获取所有id似乎是a(一个复杂的解决方案,b(不容易做到,因为在事务期间可能会发生回滚(这将导致序列id不被使用(。
有人有解决方案吗?
经过大量搜索,我找到了合适的关键词来搜索。。。"事务提交时间戳"导致各种事务时间戳跟踪和系统列,如xmin:
https://dba.stackexchange.com/questions/232273/is-there-way-to-get-transaction-commit-timestamp-in-postgres
这篇文章有一些更详细的信息:
关于Postgres track_commit_timestamp(pg_xact_commit_time stamp(的问题
简而言之:
- 您可以打开postgresql选项来跟踪提交的时间戳,并比较这些时间戳,而不是事务中的current_timestamps/clock_timestaps
- 然而,它似乎只在事务完成时才被跟踪,而不是在提交时,这使得解决方案不是防弹的。还有其他需要考虑的问题,例如事务id(xmin(滚动
- 逻辑解码/复制是寻找合适解决方案的方法
感谢所有试图帮助我找到答案的人。我希望这个总结对将来的人有用。