失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > python django并发访问挂掉 解决django高并发时数据库连接量过大的问题(实现连接池

python django并发访问挂掉 解决django高并发时数据库连接量过大的问题(实现连接池

时间:2023-04-10 19:54:05

相关推荐

python django并发访问挂掉 解决django高并发时数据库连接量过大的问题(实现连接池

Django默认每访问一次数据库都会创建一个新的数据库连接,执行完数据库操作后再关闭连接。这在高并发的场景下会导致连接数不断增多,最终出现“too many connections”错误。

如果我们使用gunicorn的gevent模式运行项目,那么不能在项目中使用threading(多线程)模块进行数据库操作。因为gevent会给threading模块动态打补丁,这会导致数据库连接无法复用,出现“too many connections”错误。

要解决以上问题,我们需要实现一个数据库的连接池。实现连接池的方法参考了https://yunsonbai.top//07/02/gunicorn-gevent-django/,这里做了一些调优。实现的步骤如下:

1. 在项目的settings.py中配置数据库连接参数,使用自定义的数据库引擎

DATABASES = {

'default': {

'ENGINE': 'myproject.mysql', # 注意这里必须是.mysql结尾

'POOL_SIZE': 20, # 每个进程的连接池的大小,总连接数=20*总进程数

'NAME': 'test',

'USER': 'root',

'HOST': '127.0.0.1',

'PASSWORD': '******',

'STORAGE_ENGINE': 'INNODB',

'PORT': '3306',

'CHARSET': 'utf-8',

'CONN_MAX_AGE': 28790, # 比mysql默认的wait_timeout小10秒

'OPTIONS': {

'init_command': 'SET default_storage_engine=INNODB',

}

}

}

2. 在项目中增加自定义的连接池模块

以项目的根目录为myproject为例,新建以下两个文件:

myproject/mysql/__init__.py

myproject/mysql/base.py

这里的文件路径mysql/base.py是固定搭配,文件夹、文件的名称和层级关系不能变,否则会造成django及其插件导入数据库引擎失败,从而自动使用默认的数据库引擎。

__init__.py是空文件,当然也可以填充其他内容。base.py的内容如下:

# -*- coding: utf-8 -*-

import random

from django.core.exceptions import ImproperlyConfigured

try:

import MySQLdb as Database

except ImportError as err:

raise ImproperlyConfigured(

'Error loading MySQLdb module.\n'

'Did you install mysqlclient?'

) from err

from django.db.backends.mysql.base import *

from django.db.backends.mysql.base import DatabaseWrapper as _DatabaseWrapper

class DatabaseWrapper(_DatabaseWrapper):

def get_new_connection(self, conn_params):

pool_size = self.settings_dict.get('POOL_SIZE') or 1

return ConnectPool.instance(conn_params, pool_size).get_connection()

def _close(self):

return None # 覆盖掉原来的close方法,查询结束后连接不会自动关闭

class ConnectPool(object):

def __init__(self, conn_params, pool_size):

self.conn_params = conn_params

self.pool_size = pool_size

self.connects = []

# 实现连接池的单例

@staticmethod

def instance(conn_params, pool_size):

if not hasattr(ConnectPool, '_instance'):

ConnectPool._instance = ConnectPool(conn_params, pool_size)

return ConnectPool._instance

def get_connection(self):

if len(self.connects) < self.pool_size:

new_connect = Database.connect(**self.conn_params)

self.connects.append(new_connect)

return new_connect

index = random.randint(0, self.pool_size - 1) # 注意这里和range不一样,要减1

try:

self.connects[index].ping()

except:

self.connects[index] = Database.connect(**self.conn_params)

return self.connects[index]

3. 调整mysql的配置

调整mysql的max_connections,使其大于项目的总连接数(连接池大小*服务进程数),修改"/etc/mysql/f",在[mysqld]配置项下设置max_connections=1000,然后重启mysql的服务。查看配置结果的sql:

SHOW VARIABLES LIKE "max_connections";

配置settings的CONN_MAX_AGE参数,使其小于数据库的wait_timeout配置,查看wait_timeout的sql:

SHOW GLOBAL VARIABLES LIKE "%wait%";

4. 验证结果

启动项目后访问几个会与数据库交互的页面,然后查看连接的数量,验证连接池的效果,查看连接数量的sql:

SHOW processlist;

python django并发访问挂掉 解决django高并发时数据库连接量过大的问题(实现连接池的方法)...

如果觉得《python django并发访问挂掉 解决django高并发时数据库连接量过大的问题(实现连接池》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。