【问题标题】:What if I don't close the database connection in Python SQLite如果我不关闭 Python SQLite 中的数据库连接怎么办
【发布时间】:2012-03-22 15:22:31
【问题描述】:

我正在做这样的事情......

conn = sqlite3.connect(db_filename)

with conn:
    cur = conn.cursor()
    cur.execute( ... )

with 自动提交更改。但是文档对关闭连接只字未提。

实际上我可以在后面的语句中使用conn(我已经测试过)。因此,上下文管理器似乎没有关闭连接。

我是否必须手动关闭连接。如果我让它打开怎么办?

编辑

我的发现:

  • 上下文管理器中的连接未关闭,我已经测试并确认了。在__exit__ 上,上下文管理器仅通过执行conn.commit() 来提交更改
  • with connwith sqlite3.connect(db_filename) as conn 相同,因此使用其中任何一个都可以保持连接处于活动状态
  • with 语句不会创建新范围,因此在 with 套件内创建的所有变量都可以在其外部访问
  • 最后,您应该手动关闭连接

【问题讨论】:

  • 如果你让它保持打开状态,它会一直保持打开状态,直到它超出范围并被垃圾收集。那时它可能安全关闭(我相信sqlite3 会这样做)。但安全总比后悔好。当您不再使用它们时关闭它们。
  • 很高兴看到有 6 个代表的 SO 用户回来并反驳他们认为没有回答问题的答案。那里有一个很大的 +1。

标签: python sqlite database-connection


【解决方案1】:

在回答如果不关闭 SQLite 数据库会发生什么的具体问题时,答案非常简单,适用于在任何编程语言中使用 SQLite。当连接被代码显式关闭或程序退出隐式关闭时,任何未完成的事务都会回滚。 (回滚实际上是由下一个打开数据库的程序完成的。)如果没有未完成的事务打开,则什么也不会发生。

这意味着您不必太担心在进程退出之前总是关闭数据库,并且您应该注意确保启动它们并在适当的时间提交的事务。

【讨论】:

  • 一个长时间运行的进程,比如一个 web 应用程序,在退出时没有隐式关闭,因为没有退出。
【解决方案2】:

您在这里有一个有效的潜在担忧,但是了解 sqlite 的运作方式也很重要:

1. connection open
    2. transaction started
        3. statement executes
    4. transaction done
5. connection closed

数据正确性方面,您只需要担心事务而不需要打开句柄。 sqlite 仅在事务(*)或语句执行中对数据库持有锁。

但是在资源管理方面,例如如果您打算删除 sqlite 文件或使用太多的连接,您可能会用完文件描述符,那么您也应该关心打开的事务外连接。

关闭连接有两种方式:要么显式调用.close(),之后仍然有句柄但不能使用它,要么让连接超出范围并被垃圾回收。

如果您必须关闭连接,请根据 Python 的格言“显式优于隐式”显式关闭它。

如果您只是检查代码的副作用,让最后一个保存对连接的引用的变量超出范围可能是可以接受的,但请记住,异常会捕获堆栈,因此会捕获该堆栈中的引用。如果您传递异常,则可以任意延长连接寿命。

caveat programmator,sqlite 默认使用“延迟”事务,即事务仅在您执行语句时开始。在上面的示例中,事务从 3 运行到 4,而不是从 2 运行到 4。

【讨论】:

  • 我猜资源管理部分在使用内存数据库时不适用(sqlite3.connect(':memory:'))?我希望在那种情况下不会使用文件描述符。
  • @JaanusVarus 在使用:memory: 数据库时确实没有分配文件描述符。我不能确定内部资源,我的意思是,毕竟内存:) 请注意,当最后一个连接关闭时,内存数据库会被删除,所以对于 OP 用例和:memory:,他们必须保持至少 1 个连接打开以保留数据。
【解决方案3】:

这是我使用的代码。由于contextlib.closing()ConnectionCursor 将自动关闭。由于上下文管理器,Connection 将自动提交。

import sqlite3
import contextlib

def execute_statement(statement):
    with contextlib.closing(sqlite3.connect(path_to_file)) as conn: # auto-closes
        with conn: # auto-commits
            with contextlib.closing(conn.cursor()) as cursor: # auto-closes
                cursor.execute(statement)

【讨论】:

  • 最里面的with有什么意义?释放一些内存?如果您不使用该上下文管理器,您会遇到什么样的问题?如果您不返回cursor,那么仅从函数返回不是等价的吗?
  • @MarkusvonBroady 显式关闭Cursor 对象是防止操作错误的好习惯。 See this post 了解更多信息。
  • @howdoicode 我没有看到任何关于您声称的良好做法的来源。我对这个想法持开放态度,但也持怀疑态度。自从 OP 提出这个问题以来,十年即将过去,我们仍然没有可靠的消息来源来说明该主题的良好做法或深入的解释为什么。我对引用 Python 的 Zen of Explicitness 持保留态度,因为在 Python 中显式释放动态数据或将对象标记为不可用并不符合习惯。这就是 GC 和 __del__ 的用途。 Dima 的最后第二段令人不安,因为它暗示了语言的缺陷。
【解决方案4】:

您可以像这样使用with 块:

from contextlib import closing
import sqlite3

def query(self, db_name, sql):
    with closing(sqlite3.connect(db_name)) as con, con,  \
            closing(con.cursor()) as cur:
        cur.execute(sql)
        return cur.fetchall()
  • 连接
  • 开始交易
  • 创建一个数据库游标
  • 执行操作并返回结果
  • 关闭光标
  • 提交/回滚事务
  • 关闭连接

在快乐和特殊情况下都安全

【讨论】:

  • 这很棒。请注意,如果您执行的操作不返回任何内容,例如插入或更新,cur.fetchall() 将只返回一个空列表。
  • 这应该在 sqlite 文档中更加突出。很恼火,我不知道这应该是标准做法。非常感谢。
【解决方案5】:

您的版本在连接使用后将 conn 留在范围内。

示例:

您的版本

    conn = sqlite3.connect(db_filename) #DECLARE CONNECTION OUT OF WITH BLOCK

    with conn:                          #USE CONNECTION IN WITH BLOCK
        cur = conn.cursor()
        cur.execute( ... )

   #conn variable is still in scope, so you can use it again

新版本

    with sqlite3.connect(db_filename) as conn:  #DECLARE CONNECTION AT START OF WITH BLOCK
        cur = conn.cursor()
        cur.execute( ... )   

   #conn variable is out of scope, so connection is closed 
   # MIGHT BE IT IS NOT CLOSED BUT WHAT  Avaris SAID!
   #(I believe auto close goes for with block)

【讨论】:

【解决方案6】:

为了管理与数据库的连接,我通常这样做,

# query method belonging to a DB manager class

def query (self, sql):
    con = sqlite3.connect(self.dbName)
    with con:
        cur = con.cursor()
        cur.execute(sql)
        res = cur.fetchall()
    if con:
        con.close()

    return res

这样做,我确定连接已明确关闭。

【讨论】:

  • 在抛出异常时不关闭连接
猜你喜欢
  • 1970-01-01
  • 2010-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多