【问题标题】:When to open/close connection to database?何时打开/关闭与数据库的连接?
【发布时间】:2012-07-17 19:33:18
【问题描述】:

我在 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”引用的连接打开了哪个确切的数据库?

【问题讨论】:

    标签: postgresql odbc


    【解决方案1】:

    长时间保持连接,但尽可能缩短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 或其他东西,并根据连接可用的结论得出结论因此在尝试下一个操作时不会失败

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

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

    【讨论】:

    • 在其他必须阅读“两次以上”的内容中,如果我理解得很好,您建议将“ROLLBACK”放在程序的 Catch 部分吗?
    • @user973238 我不会说 Visual Basic,所以我不知道那是什么意思。我的意思是,如果用户通过一个三步表单,您在每一步之后发出UPDATE 语句,请将您在第一个和第二个UPDATEs 中发送的信息保留在内存中,因为第三个可能会失败并且您必须能够重试所有三个步骤。或者让用户重新做他们所有的工作。仅在交易COMMITs 后丢弃信息。
    • @user973238 添加了伪代码解释。只是一个快速的,因为我很累,但希望它有所帮助。
    • SELECT current_database() 按预期工作,非常感谢。在我填充数据库的具体示例中,我有“创建表”和 100000 个循环插入。当(如果)程序抛出错误时,则在“try”的“catch”块中完成。在这个块中,我有“ROLLBACK TO SAVEPOINT my_savepoint”,因此所有更改都可能被取消,直到下一个用户尝试。但是现在的问题是,如果在长时间运行期间服务器发生故障并且无法执行回滚怎么办?那么操作会保持“一半完成”吗?或者这里有postgres提供的一些解决方案?还是我错过了一些非常重要的事情?
    • @user973238 一个显式事务(你以BEGIN 开始的事务)将永远在没有你显式发送COMMIT 的情况下提交。因此,如果服务器出现故障,您将失去连接,或者其他任何事情,数据库服务器会丢弃半成品。它保证永远完全消失,就好像它从未发生过一样。没有办法找回它,所以如果你想再次尝试它,你需要记住你在应用程序中所做的一切,直到你运行 COMMIT 并获得成功的返回码。
    【解决方案2】:

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

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

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

    【讨论】:

    • 我还是 postgreSQL 的新手。是否可以通过 ODBC 使用连接池?有什么例子吗?
    • 您的 ODBC 连接可以与 pgpool、pgbouncer 或其他任何东西通信。完全没问题。
    • 我会为 pgbouncer 添加一票。它易于配置且坚固可靠。
    猜你喜欢
    • 1970-01-01
    • 2014-06-11
    • 2010-11-19
    • 2019-12-31
    • 2017-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-28
    相关资源
    最近更新 更多