You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何减少多线程Django请求创建的数据库连接数?

Django多线程场景下数据库连接过多问题

项目代码结构

thread_pool = ThreadPoolExecutor()

def my_background_thread(model: MyModel): 
     # Other DB Reads here
     ...

     # Some long running action here, spawning several threads
     ...
    
     # Save result of action to DB 
     model.field = val 
     model.save()

     # Other DB writes 
     ...
   
    

def my_view(request):
   model = MyModel.objects.get(...)
   
   thread_pool.submit(my_background_thread, model)

   # Return before thread has completed 
   return HttpResponse("success")

问题描述

当前设置下,Django创建了过多不必要的数据库连接,超出了数据库的连接限制。原本瓶颈应该是多线程的IO密集型工作,数据库负载相对较小,但过多的数据库连接或连接持续时间过长反而成为了IO密集型工作的瓶颈。

疑问点

  • 将model传入新线程时,是否会在此时创建新连接?仅在读写model时创建?还是仅在写入时创建?
  • 如何查看当前打开的连接数,或追踪连接创建的时机?

核心诉求

简言之,如何避免在此场景下Django创建过多数据库连接?


解决方案与问题解答

关于数据库连接创建时机的疑问

  • 把model传入新线程不会立刻创建新连接。Django的数据库连接是线程绑定的,每个线程第一次执行数据库操作(不管是读还是写)时,才会创建新的数据库连接。传入model对象本身只是传递了一个Python对象,此时还没有触发任何数据库操作,也就不会创建连接。只有当你在新线程里执行model.save()、其他DB读写代码时,才会为这个线程创建专属的数据库连接。

如何查看/追踪连接数

  1. 数据库层面直接查看:
    • PostgreSQL:执行SELECT count(*) FROM pg_stat_activity WHERE datname = '你的数据库名';
    • MySQL:执行SHOW STATUS LIKE 'Threads_connected';
      这些命令能直接获取当前所有活跃的数据库连接数。
  2. Django内部追踪:
    • 代码中打印连接状态:
      from django.db import connections
      # 在需要追踪的位置添加
      for conn in connections.all():
          print(f"连接别名: {conn.alias}, 是否可用: {conn.is_usable()}, 连接ID: {conn.connection.thread_id() if conn.connection else '无'}")
      
    • 开启数据库日志追踪:在settings.py中配置:
      LOGGING = {
          'version': 1,
          'disable_existing_loggers': False,
          'handlers': {
              'console': {
                  'class': 'logging.StreamHandler',
              },
          },
          'loggers': {
              'django.db.backends': {
                  'handlers': ['console'],
                  'level': 'DEBUG',
                  'propagate': False,
              },
          },
      }
      
      开启后控制台会输出数据库连接的创建、关闭日志,方便追踪时机。

避免过多数据库连接的方案

1. 手动关闭线程内的数据库连接

Django默认在请求结束时自动关闭连接,但后台线程不属于请求生命周期,连接不会自动回收。在后台线程的数据库操作完成后,手动关闭连接:

def my_background_thread(model: MyModel): 
     try:
         # 其他DB Reads here
         ...

         # 长时间IO操作
         ...
        
         # 保存结果到DB
         model.field = val 
         model.save()

         # 其他DB writes 
         ...
     finally:
         # 手动关闭当前线程的数据库连接
         from django.db import connections
         for conn in connections.all():
             conn.close()

线程结束后连接会立刻释放回连接池,不会持续占用。

2. 限制线程池大小

线程池的线程数不要设置过大,建议根据数据库最大连接数调整。比如数据库最大连接数为100,线程池大小可设为30-50(预留部分连接给主请求线程):

# 设置线程池最大线程数为40
thread_pool = ThreadPoolExecutor(max_workers=40)

过多线程会导致每个线程都创建独立连接,直接打满数据库连接限制。

3. 使用第三方数据库连接池(推荐)

Django原生连接管理对长期后台线程不够友好,可使用django-db-connection-pool复用连接,避免频繁创建新连接:

  • 安装后在settings.py配置:
    DATABASES = {
        'default': {
            'ENGINE': 'django_db_conn_pool.backends.postgresql',  # 根据数据库选择对应引擎
            'NAME': 'your_db',
            'USER': 'your_user',
            'PASSWORD': 'your_pwd',
            'HOST': 'your_host',
            'PORT': 'your_port',
            'POOL_OPTIONS': {
                'POOL_SIZE': 20,  # 连接池基础大小
                'MAX_OVERFLOW': 10,  # 超出池大小的临时连接数
            }
        }
    }
    
    所有线程复用连接池中的连接,大幅减少连接数量。

4. 延迟加载model数据(可选)

如果后台线程的长时间IO操作不需要model数据,可只传递model ID到后台线程,在需要时再查询:

def my_background_thread(model_id): 
     try:
         model = MyModel.objects.get(id=model_id)
         # 后续DB操作
         ...
     finally:
         from django.db import connections
         for conn in connections.all():
             conn.close()

def my_view(request):
   model_id = MyModel.objects.get(...).id
   thread_pool.submit(my_background_thread, model_id)
   return HttpResponse("success")

主线程的连接可及时释放,后台线程连接也在操作完成后关闭,减少连接占用时长。

内容的提问来源于stack exchange,提问作者cambrialas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 09:52:52