长时间保持连接,但尽可能缩短transactions。
如何找到数据库
SELECT current_database()
见postgresql info functions
具有容错能力的长连接
不要过于频繁地打开和关闭连接。抓住它们,但要做好让它们从你身下消失的准备,例如:
- 如果数据库或它所在的服务器重新启动
- 如果您和服务器之间存在状态防火墙/路由器,可能会在一段时间后忘记空闲连接跟踪关联
- 如果数据库管理员出于任何原因故意关闭您的连接
您的应用必须从容应对连接变得不可用的情况,并准备好在建立新连接后重试操作。确保限制重试次数,这样您就不会永远循环,一遍又一遍地用相同的故障重击数据库服务器。
因为连接随时可能失效,您必须始终检查每个数据库操作的错误代码,除非您的语言在数据库操作失败时会抛出异常。如果可以使用异常,则只需将整个数据库操作序列包装在单个 try/catch 中。
如果您的应用程序非常简单并且不必对用户友好,那么只需告诉用户您丢失了连接并要求他们重新启动应用程序即可。我个人认为,如果你选择这种方法,你会后悔的,但它确实使错误处理更容易。
如果您的应用一次需要多个连接,请使用connection pool 或重新设计以在单个连接上排队操作。
具有容错和重试功能的短事务
如果您愿意,您可以长时间保持连接,您应该尽可能缩短交易时间。长时间运行的事务会导致一些数据库系统出现问题,包括 PostgreSQL - 它们会降低效率,增加锁定问题的风险等。不要在用户“思考时间”内保持事务打开 - 即暂停用户交互 - 如果可能的话.如果您必须保持交易开放,请尝试设置超时,如果用户在填写表格中途决定去度假,您会在会话到期时将用户弹回开始。如果您认为我在开玩笑,那么您还没有花足够的时间来维护生产应用程序。
您的应用应为任何数据库操作失败做好准备,并能够重试。不要在数据库语句成功运行后丢弃应用程序对任务的了解;保留它直到整个事务成功提交。这样,如果您遇到序列化失败、您的 tx 被服务器取消等,您可以重新发出整个交易。
想象一下,您编写了一些典型的线性数据库访问代码(伪代码,我不会说 Visual Basic),如下所示:
BEGIN;
value = get_value1_from_user();
UPDATE something SET field = :value;
value = get_value2_from_user();
UPDATE somethingelse SET otherfield = :value;
COMMIT;
现在想象一下如果第二个UPDATE 失败会发生什么。 COMMIT 不会发生 - 如果您尝试运行它,它会改为执行 ROLLBACK - 而且您不知道用户为 get_value1_from_user() 输入的内容,因此您无法重试。
写这样的东西通常更明智:
value1 = get_value1_from_user();
value2 = get_value2_from_user();
committed = false;
retries = 3;
do:
try:
BEGIN;
UPDATE something SET field = :value1;
UPDATE somethingelse SET otherfield = :value2;
COMMIT;
committed = true;
catch ...:
retries = retries - 1;
log "Failed to commit (sometask) because of (error message from database), "+retries+" left"
while not committed and retries > 0;
if not committed:
print "Tell the user I couldn't save their work"
end if;
当然,你想聪明地重试。失败后,检查以确保连接仍然存在,如果不存在则在重试之前重新建立连接。还要检查您的语句失败的原因 - 重新尝试语法错误的语句是没有意义的。这就是SQLSTATE 的用途,在不检查消息文本的情况下区分不同类型的错误。
永远不要检查错误消息文本作为做出决定的一种方式,因为消息会被翻译,并且可能会因版本而异。
不存在“已验证”的连接
数据库开发新手的开发人员认为,他们可以通过在继续操作之前验证数据库连接来省去很多麻烦。他们发出SELECT 1 或其他东西,并根据连接可用的结论得出结论因此在尝试下一个操作时不会失败。
这是假的。 “验证”和下一个操作之间存在固有的竞争条件。不仅如此,仅仅因为您的简单验证操作成功并不意味着下一个重要的语句不会失败。您的应用应跟踪在事务期间所做的所有更改,直到提交,并在事务失败时能够重试。
另一种方法是告诉用户“糟糕,失败了。重新输入所有更改并重试”。有时这实际上是正确的做法,例如在使用乐观锁定策略时,两个正在进行的更改发生冲突,并且您无法以编程方式安全地合并它们。不过,通常最好只在幕后重试。