ZeroMQ对于数据库事务来说太快了

时间:2014-03-07 08:48:45

标签: python postgresql sqlalchemy zeromq

在Web应用程序(Pyramid)中,我在POST上创建了某些对象,这些对象需要完成一些工作(主要是从Web上获取内容)。在SQLAlchemy的帮助下,这些对象被持久化到PostgreSQL数据库。由于这些任务可能需要一段时间,因此不会在请求处理程序内部完成,而是卸载到不同主机上的守护程序进程。创建对象后,我将其 ID (客户端生成 UUID )并通过ZeroMQ将其发送到守护程序进程。守护进程接收ID,从数据库中获取对象,它是否正常工作并将结果写入数据库。


问题:
守护程序可以在创建事务提交之前收到ID。由于我们使用pyramid_tm,所以当请求处理程序返回时没有错误地提交所有数据库事务,我宁愿这样做。在我的开发系统中,一切都在同一个盒子上运行,因此ZeroMQ闪电般快速。在生产系统上,这很可能不是问题,因为Web应用程序和守护程序在不同的主机上运行,​​但我不想指望这一点。

这个问题最近才出现,因为我们之前使用的MongoDB的write_convern为2.只有两个数据库服务器,实体上的write始终阻止了Web请求,直到实体被持久化为止(这显然不是最好的主意。)

  • 是否有人遇到类似问题?
  • 您是如何解决的?

我看到了多种可能的解决方案,但大多数解决方案并不让我满意:

  • 在触发ZMQ消息之前手动刷新事务。但是,我目前使用SQLAlchemy after_created事件触发它,这非常好,因为它完全解耦了这个过程,从而消除了“忘记”告诉守护进程工作的风险。还认为我仍然需要在守护进程端有READ UNCOMMITTED隔离级别,这是正确的吗?
  • 在处理对象之前,向ZMQ消息添加时间戳,使接收消息的工作线程等待。这显然限制了吞吐量。
  • 彻底清洗ZMQ并简单地轮询数据库。不要!

2 个答案:

答案 0 :(得分:0)

这接近你的第二个解决方案:

创建一个缓冲区,从那里删除zeromq消息中的id,让工作者定期轮询这个id-pool。如果它无法从数据库中检索id的对象,请让id位于池中直到下一次轮询,否则从池中删除id。

以某种方式处理系统的异步行为。当id在数据库中持久存在之前不断到达时,无论是否汇集id(并重新轮询相同的id)都会降低吞吐量,因为瓶颈更早。

一个好处是,你可以在此前面运行多个前端。

答案 1 :(得分:0)

我只会使用PostgreSQL的LISTEN and NOTIFY功能。 worker可以连接到SQL服务器(它已经必须这样做),并发出相应的LISTEN。然后,PostgreSQL会在相关交易完成时通知它。触发在SQL服务器中生成通知甚至可能会在有效负载中发送整行,因此工作人员甚至不需要请求任何内容:

CREATE OR REPLACE FUNCTION magic_notifier() RETURNS trigger AS $$
BEGIN
  PERFORM pg_notify('stuffdone', row_to_json(new)::text);
  RETURN new;
END;
$$ LANGUAGE plpgsql;

有了它,只要它知道有工作要做,就会有必要的信息,所以它可以在没有另一次往返的情况下开始工作。