mysql.connector 应该如何与 Django 应用程序中的线程正确共享?

How should mysql.connector be shared correctly with threads in Django applications?

提问人:spraff 提问时间:11/16/2023 更新时间:11/16/2023 访问量:24

问:

这涉及到我之前的问题的潜在解决方案,在该问题中,我在将 mysql 游标包装在 和 中得到。commands out of sync; you can't run this command now__enter____exit__

我在其他地方读到过,Django 不会生成新线程来处理多个请求,而是通常会生成多个 Django 进程以实现并行性。尽管有这个建议,我还是开始打印,并注意到它们正在发生变化。Thread.identThread.native_id

基于此,我推测性地写了这样的话:

def db_connect ():

  t = threading.current_thread()

  if not hasattr (t, 'db_connection'):
    print (f"New MyDBConnectionClass for {t.ident}~{t.native_id}")
    t.db_connection = MyDBConnectionClass ()

  return t.db_connection.get_cursor () # This object has a __exit__ which closes the cursor

以及

class MyDBConnectionClass ():

  def __del__(self):
    t = threading.current_thread()
    print (f"MyDBConnectionClass deleted for {t.ident}~{t.native_id}")

视图处理程序对游标的用法保持不变:

with db_connect() as db:
  results = db.all (query, args)

到目前为止,这似乎已经修复了错误(以及原始问题中提到的退出代码 245 崩溃)。commands out of sync

MyDBConnectionClass.__delete__未被调用,但为几个不同的值创建了各种实例。ident

我目前的假设是,存在某种线程池,并且我的应用程序不可预测地崩溃,因为有时(通常)会在创建初始连接的线程中处理视图,但有时不会。这个实验似乎表明,给每个线程一个不同的连接是有效的,这是有道理的,但我不满意,因为:

  • Thread 对象显然没有被删除(或者其他东西被阻止被调用)MyDBConnection.__del__
  • 这与我在其他地方读到的文档和示例不一致
  • 它很乱,据我所知,Django 的设计相当干净——我想我一定遗漏了什么

那么,处理mysql连接和游标对象的正确方法是什么,以便我可以这样做

with my_connection_wrapper.get_cursor() as my_cursor_wrapper:
  my_cursor_wrapper.foo ()

在 Django 视图中自由地不泄漏资源,也不会导致线程亲和不稳定问题(假设这真的是问题所在)?

python mysql django 多线程

评论


答:

1赞 Bill Karwin 11/16/2023 #1

你不能在线程之间共享数据库连接,除非你使用一些互斥锁或其他东西来同步线程。即,当一个线程使用 db 连接时,其他线程必须等待。这破坏了运行并行线程的好处。

常见的替代方法是让每个线程打开自己的 db 连接,以便它们各自在各自的 db 连接上都有自己的状态。

MySQL Connector/Python 为此目的提供了连接池。阅读 https://dev.mysql.com/doc/connector-python/en/connector-python-connection-pooling.html

这个想法是,应用程序有一个固定的数据库连接池,每个线程都可以请求从该池“借用”连接。线程执行其查询,然后尽快将连接释放回池,供另一个线程使用。close()

评论

0赞 spraff 11/16/2023
如何确保线程池足够大?
0赞 Bill Karwin 11/16/2023
连接池,而不是线程池。Python 应用的每个进程同时运行多少个线程?
0赞 spraff 11/16/2023
我不知道。我只是发现这显然是一回事。
0赞 Bill Karwin 11/16/2023
好吧,从小处着手。我会尝试 5。正如您在阅读我链接到的手册页时可能会注意到的那样,如果您的代码从池中请求数据库连接并且没有任何可用连接,则该函数会抛出一个 ,因此您应该在代码中检查它,并在必要时重试。connect()PoolError
1赞 Bill Karwin 11/16/2023
另一种策略是,您必须编写代码以“礼貌”地对待连接。我的意思是,如果你不需要连接,就不要请求连接,一旦你的代码完成了它的SQL工作,就尽快关闭连接。把它想象成从图书馆借一本书。:-)如果您的代码以这种方式短暂地使用数据库连接,则小型池可以很容易地为大量线程提供服务。