Django并发情况下数据库操作异常、连接数过多、游标问题等 解决方案
使用 siege 对项目接口进行并发测试时,后台日志出现下面几种异常:
- _io.BufferedReader
- Packet sequence number wrong
- NoneType' object has no attribute 'settimeout'
- Too many connections
运行环境:
ubuntu 16.04
python 3.6
Django 2.14
MySQL 5.6
部署 Gunicorn
数据库包 Pymysql
问题分析:
- Django 数据库访问默认是长连接,并发情况下连接数耗尽
- 使用 pymysql 库,性能较差,使用gevent 时 会出现游标访问异常等问题
解决方案:
- 使用连接池管理
- 使用MySQLdb库
1.使用PooledDB库是实现连接池
import MySQLdb
import threading
from dbutils.pooled_db import PooledDB
import logging
log = logging.getLogger("django")
# 用连接池来返回数据库连接
class DMysqlPoolConn:
def __init__(self, config):
self.__pool = PooledDB(**config)
def get_conn(self):
return self.__pool.connection()
class MysqlPoolInstance(object):
# 线程锁
_instance_lock = threading.Lock()
def __init__(self, *args,**kwargs):
@classmethod
def get_storage_instance(cls, connect_params):
if not hasattr(MysqlPoolInstance,'_instance'):
with MysqlPoolInstance._instance_lock:
MysqlPoolInstance._instance = DMysqlPoolConn(connect_params)
return MysqlPoolInstance._instance
2.重写数据库引擎
from django.db.backends.mysql.base import DatabaseWrapper as _DatabaseWrapper
import MySQLdb
class DatabaseWrapper(_DatabaseWrapper):
# 使用Pool连接池
def get_new_connection(self, conn_params):
conn_params['creator'] = MySQLdb
g_pool_connection = MysqlPoolInstance.get_storage_instance(conn_params)
conn = g_pool_connection.get_conn()
return conn
# 覆盖掉原来的close方法,查询结束后连接不会自动关闭
def _close(self):
return None
# 重写父类方法为空方法,因为PooledDB的conn没有autocommit属性,不重写就会报错
def _set_autocommit(self, autocommit):
3. settings.py中设置 ENGINE, CONN_MAX_AGE需要设置为None
需要删除 这句环境兼容代码(如果有的话,否则无法使用MySQLdb)
import pymysql
pymysql.install_as_MySQLdb()
######################################
# 数据库配置
######################################
DATABASES = {
'default': {
'ENGINE': 'core.mysql_engine', # 数据库引擎
'NAME': CommonConfig.database_name, # 你要存储数据的库名,事先要创建
'USER': CommonConfig.database_user, # 数据库用户名
'PASSWORD': CommonConfig.database_password, # 密码
'HOST': CommonConfig.database_host, # 主机
'PORT': CommonConfig.database_port, # 数据库使用的端口
'CONN_MAX_AGE': None,
'OPTIONS': {
'charset': 'utf8mb4',