当前位置: 首页 > 面试题库 >

何时使用MySQLdb关闭游标

乐正意智
2023-03-14
问题内容

我正在构建WSGI Web应用程序,并且有一个MySQL数据库。我正在使用MySQLdb,它提供了用于执行语句和获取结果的游标。
获取和关闭游标的标准做法是什么? 特别是,我的光标应持续多长时间?我应该为每个交易获取一个新的游标吗?

我相信您需要在提交连接之前关闭游标。查找不需要中间提交的事务集是否有任何显着的优势,这样您就不必为每个事务获取新的游标?获取新的游标是否有很多开销,还是不重要?


问题答案:

既然不知道什么是标准做法,因为这通常是不清楚和主观的,因此您可以尝试向模块本身寻求指导。通常,将with关键字用作其他用户的建议是一个不错的主意,但是在这种特定情况下,它可能无法提供预期的功能。

从模块的1.2.5版本开始,使用以下代码(github)MySQLdb.Connection实现上下文管理器协议:

def __enter__(self):
    if self.get_autocommit():
        self.query("BEGIN")
    return self.cursor()

def __exit__(self, exc, value, tb):
    if exc:
        self.rollback()
    else:
        self.commit()

已经有一些现有的问答with,或者您可以阅读了解Python的“ with”语句,但是实质上发生的是__enter__with块的开头__exit__执行,并在离开with块时执行。如果打算以后引用该对象,则可以使用可选的语法with EXPR as VAR将by返回的对象绑定 __enter__到名称。因此,在上述实现的基础上,这是查询数据库的一种简单方法:

connection = MySQLdb.connect(...)
with connection as cursor:            # connection.__enter__ executes at this line
    cursor.execute('select 1;')
    result = cursor.fetchall()        # connection.__exit__ executes after this line
print result                          # prints "((1L,),)"

现在的问题是,退出with块后连接和游标的状态是什么?__exit__上面显示的方法仅调用self.rollback()self.commit(),而这些方法都没有继续调用该close()方法。游标本身没有__exit__定义方法,也没有关系,因为with它只管理连接。因此,退出with块后,连接和游标都保持打开状态。通过在上面的示例中添加以下代码,可以很容易地确认这一点:

try:
    cursor.execute('select 1;')
    print 'cursor is open;',
except MySQLdb.ProgrammingError:
    print 'cursor is closed;',
if connection.open:
    print 'connection is open'
else:
    print 'connection is closed'

您应该看到输出到标准输出的“光标已打开;连接已打开”输出。

我相信您需要在提交连接之前关闭游标。

为什么?正如模块文档中所隐含的那样,作为基础的MySQL C
API
MySQLdb没有实现任何游标对象:“ MySQL不支持游标;但是,游标易于仿真。” 实际上,MySQLdb.cursors.BaseCursor该类直接继承object于游标,并且对提交/回滚没有任何限制。Oracle开发人员曾这样说:

在cur.close()之前的cnx.commit()对我来说最合乎逻辑。也许您可以遵循以下规则:“如果不再需要,请关闭光标。”
因此,在关闭游标之前,先执行commit()。最后,对于Connector /
Python而言,它并没有多大区别,但是对于其他数据库而言,则可能没有什么不同。

我希望这与您达到该主题的“标准实践”一样近。

查找不需要中间提交的事务集是否有任何显着的优势,这样您就不必为每个事务获取新的游标?

我对此非常怀疑,在尝试这样做时,您可能会引入其他人为错误。最好决定约定并坚持执行。

获取新的游标是否有很多开销,还是不重要?

开销可以忽略不计,完全不涉及数据库服务器;它完全在MySQLdb的实现中。如果您真的想知道创建新游标时发生了什么,可以在BaseCursor.__init__github上查看。

回到前面的讨论中with,也许现在您可以理解为什么MySQLdb.Connection__enter____exit__方法在每个with块中为您提供了一个全新的游标对象,而不必理会它或在块末尾将其关闭。它相当轻巧,纯粹是为了您的方便而存在。

如果对微管理光标对象确实很重要,则可以使用contextlib.closing来弥补光标对象没有定义__exit__方法的事实。为此,还可以使用它强制连接对象在退出with块时自行关闭。这应该输出“
my_curs已关闭; my_conn已关闭”:

from contextlib import closing
import MySQLdb

with closing(MySQLdb.connect(...)) as my_conn:
    with closing(my_conn.cursor()) as my_curs:
        my_curs.execute('select 1;')
        result = my_curs.fetchall()
try:
    my_curs.execute('select 1;')
    print 'my_curs is open;',
except MySQLdb.ProgrammingError:
    print 'my_curs is closed;',
if my_conn.open:
    print 'my_conn is open'
else:
    print 'my_conn is closed'

注意,with closing(arg_obj)不会调用参数对象的__enter____exit__方法。它
只会closewith块的末尾调用参数对象的方法。(要查看实际情况,只需Foo使用__enter____exit__close包含简单print语句的方法定义一个类,然后将执行时的操作with Foo(): pass与执行时的操作进行比较with closing(Foo()): pass。)这有两个重要的含义:

首先,如果启用了自动提交模式,则BEGIN当您with connection在块末尾使用并提交或回滚事务时,MySQLdb将在服务器上进行显式事务。这些是MySQLdb的默认行为,旨在保护您免受MySQL的立即提交任何DML语句的默认行为的影响。MySQLdb假定在使用上下文管理器时需要事务,并使用显式BEGIN绕过服务器上的自动提交设置。如果您习惯于使用with connection,您可能会认为自动提交实际上只是被绕过了而被禁用了。如果添加,可能会给您带来不愉快的惊喜closing您的代码并失去交易完整性;您将无法回滚更改,您可能会开始看到并发性错误,并且可能并不清楚为什么。

第二,with closing(MySQLdb.connect(user, pass)) as VAR结合的 连接对象
VAR,在对比with MySQLdb.connect(user, pass) as VAR,其结合 一个新的光标对象
VAR。在后一种情况下,您将无法直接访问连接对象!相反,您将必须使用游标的connection属性,该属性提供对原始连接的代理访问。关闭游标时,其connection属性设置为None。这将导致废弃的连接一直存在,直到发生以下情况之一:

  • 删除所有对光标的引用
  • 光标超出范围
  • 连接超时
  • 通过服务器管理工​​具手动关闭连接

您可以通过监视打开的连接(在Workbench中或使用SHOW PROCESSLIST)进行测试,同时一步一步地执行以下几行:

with MySQLdb.connect(...) as my_curs:
    pass
my_curs.close()
my_curs.connection          # None
my_curs.connection.close()  # throws AttributeError, but connection still open
del my_curs                 # connection will close here


 类似资料:
  • 在Python的sqlite3模块里,如果只关闭了sqlite的链接,不关闭游标,会有什么事嘛?

  • 问题内容: 我的应用程序需要执行以下操作: 打开一个,并获取基础(通过) 根据以上内容创建新对象 到目前为止,我只需要一个,所以我过去通过调用原始流(即,我调用的流)来关闭它。我之所以使用它,是因为某些Android API方法具有此类参数。 现在我将同时拥有更多对象,什么时候关闭?(我的猜测:何时所有对象都关闭了?) 问题答案: 我相信你是对的。小型测试表明,在关闭后,该变得无效。请注意,如果有

  • 我有一个单身汉需要开始执行计划。以下是代码: 我不确定何时或是否应该调用执行器的关机方法。我使用的是JEE5,所以我不确定仅仅取消部署应用程序是否会自动执行关机,或者我是否在浪费大量时间,创建大量线程,并且没有杀死它们。 -编辑- 我会再补充一些信息,以防万一。 整个应用程序是一个RESTful web应用程序,使用Jersey作为ServletContainer。

  • 我不想更改端口号,但收到异常“exception in thread”main“java.net.BindException:Address already in use:JVM_Bind” ServerSocket服务器=新服务器套接字(1234180);

  • 问题内容: 我必须处理一个很大的结果集(可能是数十万行,有时更多)。 不幸的是,它们需要一次全部检索(在启动时)。 我正在尝试通过使用尽可能少的内存来做到这一点。 通过查看SO,我发现使用可能是我想要的,但我仍然不太了解如何正确使用它们。 正在做一个从基光标或SScursor相同的(在内存使用的术语)? 我可以一排(或几排)从sscursor的行中“流化” 吗?如果是,这样做的最佳方法是什么? 问

  • 问题内容: 我已经安装了python 2.7 64bit,MySQL-python-1.2.3.win-amd64-py2.7.exe。 我使用以下代码插入数据: 这导致将n打印为1,但在mysql客户端数据中不可见。 谷歌说我必须添加。 但是我不知道为什么MySQLdb将其关闭; 问题答案: 我不知道在GAE中使用自动提交是否有特定的原因(假设您正在使用它)。否则,您可以手动提交。 请注意,您可