我们运行postgres服务器v9.2.8,并使用epgsql(erlang)作为客户端库。在某些情况下,我们在生产中使用但无法在开发环境中重现,我们会丢失数据。
我们的应用程序中的一个函数(应该被杀死)允许操作员在正在运行的连接上更改会话参数。由于连接通常总是在生产中忙碌,所以" SET SESSION bla-bla"查询总是崩溃pgsql_connection进程。
在崩溃之前,pgsql_connection发送一个"终止" (' X')通过pgsql_sock(tcp套接字的包装器)向后端发送信号。同时另一个erlang进程(让我们称之为" worker")正在等待postgres后端使用相同套接字的响应。
现在的问题是:收到"终止"来自客户端的信号,后端即使已发送" OK" on" COMMIT"声明已经?
因为如果 可能,工作人员将有机会向主申请流程报告成功写入的交易,而事务确实已被取消。
或者,我在哪里可以阅读更多有关此内容的详细信息?文档说(http://www.postgresql.org/docs/9.2/static/protocol-flow.html):
对于正常或异常终止,任何开放交易都是 回滚,没有承诺。然而,应该注意的是,如果是前端 在处理非SELECT查询时,后端断开连接 可能会在注意到断开连接之前完成查询。如果 查询在任何事务块之外(BEGIN ... COMMIT序列) 然后它的结果可能会在断开连接之前提交 识别。
- 不是一个清晰的陈述。
答案 0 :(得分:1)
现在的问题是:收到"终止"来自客户端的信号,后端即使已发送" OK" on" COMMIT"声明已经?
没有。这根本不可能。如果它已经提交,它已经提交,并且没有回头路。那是什么"承诺"装置
Pg可能在提交命中磁盘之前返回成功的唯一时间是持久性的,如果你通过设置synchronous_commit = off
告诉它。
如果您发现任何不同的情况,那么很可能是因为尝试在多个进程之间共享单个连接(在fork()
之前建立连接)而没有正确锁定或其他互斥以确保命令在飞行中时锁定连接。
请注意反之并非如此,这可能是您在引用的文档段落中所考虑的内容。如果客户端在发出commit命令后消失(崩溃,丢失连接等),则事务可以提交,而不会向客户端返回成功的OK。
应用程序正在做什么,它在有线协议上发送不同步消息的地方完全被破坏了。它保证会导致不可预测的问题。该协议有点健壮,因此您不太可能得到像非预期commit
这样的内容,但您很可能会中断交易或突然断开整个会话。
如果您需要能够回滚/中止已提交的事务,那么您的应用程序设计就会出现问题。当你说COMMIT
时,你还没准备好提交。如果应用程序进程崩溃或整个服务器在Pg提交事务和你做任何你需要做的事情之间崩溃,你会遇到同样的问题。
如果您无法修复应用程序设计以避免这种情况,则必须使用两阶段事务,直接使用PREPARE TRANSACTION
然后COMMIT PREPARED
,或间接使用XA API。这在性能和管理开销方面会产生很大的成本,但如果您需要在数据库提交后进行特殊工作但是在您真正完成"完成之前,这是唯一的选择。
您引用的文档是在讨论应用程序发送COMMIT
但在收到后端确认提交之前断开连接的情况。因为TCP / IP是缓冲的,所以不能保证COMMIT
被刷新到Pg,如果它确实存在,那么就不能保证它不会伴随RST
终止连接。因此,在这种特定情况下,交易是否会提交有些不确定。这是一个问题的应用程序需要有一种方法来检查在恢复工作时是否提交了最后一个工作单元,或者它是否能够使用两阶段事务。你引用的文档没有说明在完成提交后能否取消提交,因为你无法做到。如初。
假设应用程序在提交后必须执行某种额外的工作,例如移动文件或发送电子邮件或在其他数据存储上工作,那么您可能需要进行两阶段交易。即使这样,除非分布式事务中的所有各方支持两阶段提交,否则您很容易受到问题的影响,因为您的"其他位"可以完成,然后您的工作人员或服务器可能会崩溃,然后确认完成将被发送到数据库以完成提交的第二阶段。
您可以在DB中保留自己的两阶段提交日志,而不是使用真正的2PC:
主数据库是否正常工作并将记录写入工作日志表中,其中表示"我已完成数据库中的工作,我即将完成下一部分&# 34;
做下一部分;以及
更新工作日志,说明下一部分已完成。
...但是这有同样的问题,第2部分和第3部分之间的崩溃导致应用程序忘记它执行了第2部分并在启动时重复它。如果你不能忍受这种情况,你需要找到一种方法来使第2部分提交完成可验证,这样你就可以判断它是否已经完成,或者找到一种方法使它能够做到2 - 阶段提交。
要了解有关此主题的更多信息,请阅读XA,分布式事务,两阶段提交等。