Postgres LISTEN/NOTIFY - 低延迟、实时?

3

我计划使用Postgres的LISTEN/NOTIFY方法来获取表中记录的插入时间(实际事务提交时间),为了实现这个目标,我计划按照以下步骤进行操作。在插入时,我会发出通知,具体如下。

BEGIN;
  INSERT INTO table_name(id, ...) values (id,....);
  select pg_notify('test_channel', 'id - ' || id || ' trans start time - ' || now() || ' notify start time - ' || clock_timestamp()); 
END;

然后我计划使用https://pythonhosted.org/psycopg2/advanced.html#asynchronous-notifications来接收这些通知。我想找出确切的事务提交时间(记录可读取的时间),精确到微秒级别。我知道NOTIFY(pg_notify)实际上是在事务提交后立即发送通知,但我无法找出它发生的确切时间。我在NOTIFY中拥有的时钟时间戳值不是实际的事务提交时间。我猜测我监听通知的时间会接近事务提交时间,但我不确定它有多接近。首先,在我监听时,我的代码之间会有一些时间轮询(无论多么小),其次,我不确定NOTIFY/LISTEN通信本身是否存在滞后。有什么想法吗?我们有一个阅读器按批次选择行,使用“检查点”时间,每个批次获取上一个批次中最后一个时间戳之后的行,但我们丢失了一些行。(原因:时间戳值基于INSERT发生的时间(00.00.00)。在负载较重时,如果事务需要更长时间,它将在10秒后被插入(00.00.10),如果阅读器在那10秒内读取并找到其INSERT时间比row1晚(00.00.05)的行,则会错过这一行(row1)。问题的完整描述类似于此博客中写的问题:http://blog.thefourthparty.com/stopping-time-in-postgresql/)

嗯...为什么?你想通过这个实现什么?你试图解决的根本问题是什么,你需要它的原因是什么? - Craig Ringer
我已经在描述中更新了我们试图解决的问题。 - Chandra
1
所以你只是想实现一个可靠的队列,有多个写入者和一个读取者?试图通过这种方式修复乱序提交和可见性问题是行不通的。考虑在实际的基础主题上发布一个新的、独立的问题,即如何避免队列读取器在扫描表后进行正在进行的事务提交时丢失行。很让人沮丧的是,你之前忽略了我的这个问题。 - Craig Ringer
在这里发布了一个单独的问题。https://dev59.com/5FwY5IYBdhLWcg3wJk8q - Chandra
2个回答

2
我想了解的是事务提交发生的确切时间(记录可供读取),精确到微秒。方便的是,PostgreSQL 9.5刚刚添加了支持提交时间戳的功能。请参阅提交时间戳。请注意,您必须启用track_commit_timestamp才能使用此功能,并且有关提交时间戳的信息不会永久保存,因此相当旧的行将只得到空结果。
您可以在事务期间的任何时候使用txid_current()获取事务ID,例如使用insert ... returning ...。然后,您可以在提交后的后续查询中查找提交时间戳。
对于旧版本,您应该在insert ... returning ...子句中包含clock_timestamp。它将是记录插入的时间,而不是提交时间,但这确实是可能接近的时间。
我猜测我监听通知的时间会接近事务提交时间,但我不确定有多接近。
“相当”。这将取决于网络延迟、CPU调度延迟等因素。它肯定不会精确到微秒级。
例如,在Windows上,它最多精确到毫秒,但默认情况下,它将精确到最接近的15毫秒计时器滴答声。
首先,在我的代码中监听时,存在一些轮询之间的时间(无论多小)。
不要轮询。使用select()选择套接字,这样只要有数据可读就会立即唤醒您。在Linux上,您最好使用epoll()系统调用来实现此功能。
其次,我不确定NOTIFY/LISTEN通信本身是否存在延迟。
是的,因为事务提交需要时间。因此,在您发出NOTIFY和事件发送给侦听器之间存在一些非零时间。

总结你回答中的最后三个响应......因此,如果我们排除事务提交所需的时间,因为通知会在事务提交后立即发送,如果我使用select()而不是轮询,则NOTIFY和LISTEN之间唯一的时间差是由于网络延迟、CPU调度等原因造成的。我的理解正确吗?我明白NOTIFY负载中的clock_timestamp值没有事务结束时间。 - Chandra
提交并不是瞬间完成的,只是在可见性方面具有原子性。如果您所说的是微秒级,这一点就开始起作用了。在提交设置到 clog 后,通知将在后提交工作期间发送。有关详细信息,请参阅源代码。此外,select()epoll()并不一定是零延迟的;实际上,由于内核中的延迟、系统管理中断等,它们很容易在数据变得可读和应用程序被通知之间产生毫秒级的延迟。您需要一个实时进程和低延迟内核才能获得合理的结果。 - Craig Ringer
听起来你正在尝试将PostgreSQL用作实时系统,但它并不是。它没有被设计成这样。在LWLocks中可能会有长时间的锁延迟。在文件系统操作期间,它可能会在关系扩展锁上阻塞。在检查点期间,它可能会被缓冲区钉住而延迟。各种各样的事情意味着即使延迟通常很低,它们也不是有界的,也不总是低的。如果负载很大且运气不好,您可能会在提交和处理通知之间出现大的延迟。使用您的方法,您永远不会知道。它是不可靠的。 - Craig Ringer
另外,psycopg2和Python?用于超低延迟/实时处理?你是认真的吗? - Craig Ringer
我已经更新了描述,说明我正在尝试解决的问题。 - Chandra
显示剩余2条评论

1

现在()总是小于实际时间,事务提交并对读者可见的时间(除非您将脏读作为隔离级别)。

更好的方法(不会产生竞争条件)是在事务结束后调用pg_notify()并仅在事务提交时使用clock_timestamp()。


使用NOTIFYpg_notify()没有任何区别。使用clock_timestamp代替now()(对于事务来说是current_timestamp)是一个好建议。但是我不理解您所说的“在事务结束后立即”。除非您进行另一个事务,否则在事务结束后您不能执行任何操作。 - Craig Ringer
你的意思是设置一个触发器'after insert',其中包含NOTIFY语句吗?据我理解,触发器是当前事务的一部分,并且会增加总事务时间?除此之外,我怎样才能在提交后立即添加NOTIFY呢? - Chandra
您是正确的,after insert 触发器在事务期间及作为其一部分被触发。通知由触发器排队,然后在提交后传递。 - Craig Ringer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接