如何减少多线程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读写代码时,才会为这个线程创建专属的数据库连接。
如何查看/追踪连接数
- 数据库层面直接查看:
- PostgreSQL:执行
SELECT count(*) FROM pg_stat_activity WHERE datname = '你的数据库名'; - MySQL:执行
SHOW STATUS LIKE 'Threads_connected';
这些命令能直接获取当前所有活跃的数据库连接数。
- PostgreSQL:执行
- 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
相关产品推荐
相关产品推荐

