4

我在 Windows 上通过 VB.NET/ODBC 使用 PostgreSQL 数据服务器,这似乎是存储/操作数据的非常可靠且高度可用的解决方案。
作为这里的初学者,我对何时打开/关闭与数据库的连接有些疑问。

例如,我可以在程序启动时打开连接,并在程序执行期间保持打开状态,持续时间可以是几天,甚至几个月。在这种情况下,我会传输连接参考以用于程序中的所有读/写功能。

或者,在其他情况下,我可以为每个函数调用或数据库上的操作打开/关闭连接,这不会花费很长时间,但会带来许多打开/关闭情况。

现在我想从有经验的用户那里得到更好的建议。

1)在程序执行期间始终保持打开的连接。
2)每次需要读取/保存数据时连接/关闭。

第二个但相关的问题是当我像这样打开与数据库的连接时:

    Dim conn As New OdbcConnection
    conn.ConnectionString = "Dsn=" + dbDsn + _
                            ";database=" + mydatabase + _
                            ";server=" + dbServer + _
                            ";port=" + dbPort + _
                            ";uid=" + dbUser + _
                            ";pwd=" + dbPass
    conn.Open()
    ... etc...

这里有一种方法可以稍后在程序中使用“conn”引用的连接打开哪个确切的数据库?

4

2 回答 2

14

长时间保持连接,但尽可能缩短事务。

如何找到数据库

SELECT current_database()

查看postgresql 信息函数

具有容错能力的长连接

不要过于频繁地打开和关闭连接。抓住它们,但要准备好让它们从你身下消失,例如:

  • 如果数据库或它所在的服务器重新启动
  • 如果您和服务器之间有状态防火墙/路由器可能会在一段时间后忘记空闲连接跟踪关联
  • 如果数据库管理员出于任何原因故意关闭您的连接

您的应用程序必须优雅地处理连接变得不可用,并准备好在建立新连接后重试操作。确保限制重试,这样您就不会永远循环,一遍又一遍地用相同的故障锤击数据库服务器。

因为连接随时可能变得无效,所以您必须始终检查绝对每个数据库操作的错误代码,除非您的语言在数据库操作失败时会抛出异常。如果可以使用异常,则只需将整个数据库操作序列包装在单个 try/catch 中。

如果您的应用程序非常简单并且不必对用户友好,那么只需告诉用户您失去连接并要求他们重新启动应用程序即可。我个人认为,如果你选择这种方法,你会后悔的,但它确实使错误处理更容易。

如果您的应用一次需要多个连接,请使用连接池或重新设计以对单个连接上的操作进行排队。

具有容错和重试的短事务

如果您愿意,您可以长时间保持连接,但您应该尽可能缩短交易时间。长时间运行的事务会导致一些数据库系统出现问题,包括 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为了在不检查消息文本的情况下区分不同类型的错误。

永远不要将错误消息文本作为做出决定的一种方式,因为消息会被翻译,并且可能会因版本而异。

没有“已验证”连接之类的东西

数据库开发新手的开发人员认为,他们可以通过在进行操作之前验证其数据库连接来省去很多麻烦。他们发出 a SELECT 1or something 并根据连接可用的结论得出结论,因此在尝试下一个操作时不会失败

这是假的。“验证”和下一个操作之间存在固有的竞争条件。不仅如此,仅仅因为您的简单验证操作成功并不意味着下一个重要的语句不会失败。您的应用程序应该跟踪它在事务期间所做的所有更改,直到提交,并且能够在事务失败时重试。

另一种方法是告诉用户“哎呀,失败了。重新输入所有更改并重试”。有时这实际上是正确的做法,例如当使用乐观锁定策略时,两个正在进行的更改发生冲突,并且您无法以编程方式安全地合并它们。不过,通常最好在幕后重试。

于 2012-07-17T08:12:47.500 回答
4

使用连接池,例如 pgbouncer 或 pgpool。本月早些时候我们做了一些测试:

使用 pgpool 大约需要 9 毫秒来执行查询,没有 pgpool 大约需要 700 毫秒来执行相同的查询。

区别在于查询计划的缓存。

于 2012-07-17T11:33:18.133 回答