【发布时间】:2014-11-05 08:23:33
【问题描述】:
我想创建一个 Web 服务,允许客户端获取表中的所有行,然后允许客户端仅获取新的或更新的行。
最简单的实现似乎是将当前时间戳发送给客户端,然后让客户端在以下请求中请求比时间戳更新的行。
这似乎是可行的,方法是在更新和插入触发器中保留一个时间戳设置为 NOW() 的“updated_at”列,然后查询较新的行,并传递 NOW() 的值。
问题是如果有未提交的事务,这些事务会将updated_at设置为事务的开始时间,而不是提交时间。
因此,这个简单的实现不起作用,因为行可能会丢失,因为它们可能带有过去的时间戳。
尽管这似乎是一个非常普遍的需求,但我一直无法找到任何简单的解决方案:有什么想法吗?
可能的解决方案:
在表中保留单调时间戳,在每个事务开始时将其更新为 MAX(NOW(), last_timestamp + 1) 并将其用作行时间戳。问题:这实际上意味着所有写入事务都完全序列化并锁定整个数据库,因为它们在更新时间表上发生冲突。
在事务结束时,添加从 NOW() 到更新表中时间的映射,如上述解决方案。这似乎需要显式锁定并使用序列来生成非临时“时间戳”,因为仅在单行上使用 UPDATE 会导致 SERIALIZABLE 模式下的回滚。
不知何故,PostgreSQL 在提交时迭代所有更新的行并将 updated_at 设置为单调时间戳
不知何故,PostgreSQL 自己维护了一个事务提交时间表,目前它似乎没有这样做
使用内置的 xmin 列似乎也是不可能的,因为 VACUUM 会丢弃它。
如果能够在数据库中执行此操作而无需修改应用程序中的所有更新,那就太好了。
通常的做法是什么?
天真的解决方案的问题
如果不明显,这是使用 NOW() 或 CLOCK_TIMESTAMP() 的问题:
- 在时间 1,我们在事务中运行 NOW() 或 CLOCK_TIMESTAMP(),它给出 1,我们更新行设置时间 1 作为更新时间
- 在时间 2,客户端获取所有行,我们告诉他我们在时间 2 之前提供了所有行
- 在时间 3,事务在 updated_at 字段中以“时间 1”提交
- 客户端从时间 2(他从上一次完整提取请求中获得的时间)开始请求更新的行,我们查询 updated_at >= 2 并且不返回任何内容,而不是返回刚刚添加的行
- 该行已丢失,客户将永远无法看到
【问题讨论】:
-
那还不是提交时间,所以它不起作用。 IE。事务可能会在调用 clock_timestamp() 后一秒提交,如果客户端在此期间更新,则更新丢失。
-
出于同样的原因,这也不起作用(除非您在锁定下进行)。
-
也遇到了这个问题,正在寻找答案。
-
我最好的想法是让表的更新也插入到队列表中。队列表中的作业被弹出并稍后更新“updated_at”字段。
标签: postgresql time transactions