【问题标题】:What is the proper way of using MySQLdb connections and cursors across multiple functions in Python在 Python 中跨多个函数使用 MySQLdb 连接和游标的正确方法是什么
【发布时间】:2015-07-29 02:09:03
【问题描述】:

我对 Python 及其 MySQLdb 连接器有点陌生。 我正在编写一个 API 来使用 RESTful 方法从数据库中返回一些数据。在 PHP 中,我将连接管理部分封装在一个类中,充当 MySQL 查询的抽象层。

在 Python 中:

  • 我在脚本的早期定义了连接: con = mdb.connect('localhost', 'user', 'passwd', 'dbname')

  • 然后,在所有后续方法中:

    import MySQLdb as mdb
    
    def insert_func():
    
    with con: 
    
    cur = con.cursor(mdb.cursors.DictCursor)
    cur.execute("INSERT INTO table (col1, col2, col3) VALUES (%s, %s, %s)", (val1, val2, val3) )
    
    rows = cur.fetchall()
    
    #do something with the results
    
    return someval
    

    等等

  • 我使用mdb.cursors.DictCursor,因为我更喜欢能够以关联数组的方式访问数据库列。

现在问题开始出现:

  • 在一个函数中,我发出一个插入查询来创建一个具有唯一“groupid”的“组”。

  • 此“组”有一个创建者。数据库中的每个用户都在表中他/她行的“组”列中保存一个 JSON 数组。

  • 所以当我创建一个新组时,我想将 groupid 分配给创建它的用户。

  • 我使用类似功能更新用户记录。

  • 我已将“插入”和“更新”部分包装在两个单独的函数定义中。

  • 我第一次运行脚本时,一切正常。

  • 我第二次运行脚本时,脚本无休止地运行(我怀疑是由于与 MySQL 数据库的一些空闲连接)。

  • 当我使用 CTRL + C 中断它时,出现以下错误之一:

    • “‘游标’对象没有属性‘连接’”
    • “命令不同步;您现在无法运行此命令”
    • 或任何其他 KeyboardInterrupt 异常,正如预期的那样。

在我看来,这些错误是由我的代码中处理连接和游标的一些错误方式引起的。

我读到使用with con: 是一种很好的做法,这样连接会在查询后自动关闭。我在每个函数中使用 'con' 上的 'with',因此连接已关闭,但我决定全局定义连接,以便任何函数使用它。这似乎与with con: 上下文管理不兼容。我怀疑游标需要以类似的方式“上下文管理”,但我不知道该怎么做(据我所知,PHP 不使用 MySQL 游标,所以我没有使用它们的经验)。

我现在有以下问题:

  1. 为什么第一次有效,第二次无效? (但是,它会在 CTRL + C 中断后再次工作一次)。

  2. 在使用多个函数(可以依次调用)时如何使用连接和游标?

【问题讨论】:

  • 只是为了确定-您的意思是同一个python模块中的函数,对吗?
  • 是的,没错!

标签: python mysql-python


【解决方案1】:

对于连接,对于文件句柄rule of thumb is open late, close early.

所以我建议只在他们试图做一件事的地方共享连接。或者,如果您使用多进程,那么每个进程都会获得一个连接,然后再次打开延迟,提前关闭。如果您正在执行顺序操作(例如在循环中),则在循环外打开和关闭。拥有全球连接可能会变得混乱。主要是因为现在您必须跟踪哪个函数在什么时间使用它,以及它试图用它做什么。

“现在无法运行命令”的问题是因为您的键盘中断终止了活动连接。

关于您的问题之一 - 无休止地可能在任何地方。每个 python 实例都会有自己的连接。因此,当您第二次运行它时,它应该获得自己的连接。打开一个mysql客户端然后做

show full processlist

看看发生了什么。

【讨论】:

  • 感谢您的回答。我将尝试重新考虑我的全球联系策略,以适应“迟开”早关模式。
【解决方案2】:

我认为这里有两个主要问题 - 一个似乎是 python 代码,另一个是您如何与数据库交互的结构。

首先,您没有关闭连接。这取决于您的应用程序的需求——您必须决定它应该保持打开多长时间。参考this SO question

from contextlib import closing
with closing( connection.cursor() ) as cursor:
    ... use the cursor ...

# cursor closed.  Guaranteed.

connection.close()

现在,您必须使用Ctl+C 中断您的程序,因为您的with 语句没有理由停止运行。

其次,开始从“事务”的角度考虑您与数据库的交互。做某事,将其提交给数据库,如果它不起作用,则回滚,如果它起作用,则关闭连接。 Here's a tutorial.

【讨论】:

  • 谢谢!非常有帮助。所以你会建议打开连接,在调用插入和更新函数之前启动一个事务?我是否需要将连接作为参数传递给这些函数,并根据返回值通过“提交”或“回滚”结束事务,例如?
  • commit 将始终在 try: 块下尝试。如果失败,请在except 块下执行rollback()。我认为让您的连接保持打开状态并在这些“离散”事务块中工作是可以的。如果您的连接出现问题 - 不用担心,您已经提交了所需的内容。如果出现故障,请回滚以使您的数据仍然完好,然后重试。因此,您的连接始终存在,并且仅在您明确调用它关闭时才结束(con 应该是一个全局变量,因此可以在任何地方访问)
  • 好的,非常感谢。我会尝试用这个来改进我的代码,看看它是否能解决我的问题。
猜你喜欢
  • 2012-08-09
  • 2012-11-16
  • 2015-09-27
  • 2021-02-12
  • 2011-01-22
  • 1970-01-01
  • 1970-01-01
  • 2013-02-05
  • 2014-09-25
相关资源
最近更新 更多