注:笔者的Django版本为 1.9,Python版本为 2.7
一直想在Django中实现MySQL连接池,在网上寻找良久无果,偶然之间,看到了一个帖子"让Django支持数据库长连接(可以提高不少性能哦)",受到启发,既然可以实现长连接,那么也可以实现连接池了,动手!
原理与链接帖子是一样的,只是将数据库的连接实例改为连接池的连接实例。连接池使用DBUtils实现,但在Django中,需要对DBUtils进行一点小改动。
一部分内容是与链接帖子一样的,在此为了完整性,只做一次搬运工。
第一步,对DBUtils动个小手术。
DBUtils是一个实现数据库连接池的库,支持DB-API 2接口规范,点击下载。解压下载后的压缩包,将其中的DBUtils目录加入应用程序目录中,笔者将其放在应用程序根目录"database"中,Docs、Examples、Tests这3个目录可以删除,修改SteadyDB.py文件,在类 SteadyDBConnection 中添加3个成员方法,如下:
def autocommit(self, *args, **kwargs):
self._con.autocommit(*args, **kwargs)
def get_server_info(self):
return self._con.get_server_info()
@property
def encoders(self):
return self._con.encoders
修改 PooledDB.py 和 PersistentDB.py 两个文件,将
from DBUtils.SteadyDB import connect
改为
from .SteadyDB import connect
第二步,"狸猫换太子"。
将Django中的mysql模块加入应用程序目录中,笔者将Django安装在虚拟环境中,位置是 Django虚拟环境路径/lib/python2.7/site-packages/django/db/backends,将backends目录下的 __init__.py文件和mysql目录复制到database目录下的db目录中,目录结构如下:
应用程序根目录
|
| -- database
|
| -- DBUtils
| -- db
|
| -- __init__.py
| -- mysql
在mysql目录下添加pool.py文件,如下:
# coding:utf-8
'''A connection pool of MySQL in Django 1.5 based on DBUtils.'''
import MySQLdb
from database.DBUtils.PooledDB import PooledDB
class ConnectionWrapper(object):
def __init__(self, connection):
self._conn = connection
def __getattr__(self, method):
''' 代理数据库连接的属性方法 '''
return getattr(self._conn, method)
def close(self):
''' 代理Django的关闭数据库连接 '''
self._conn.close()
class DBWrapper(object):
def __init__(self, module):
self._connection = None
self._db = module
self._pool = {}
def __getattr__(self, item):
return getattr(self._db, item)
def _clear_connections(self, **kwargs):
''' 关闭已有连接 '''
conn = MySQLdb.connect(**kwargs)
cursor = conn.cursor()
sql = ''
cursor.execute('show full processlist;')
processlist = cursor.fetchall()
for th in processlist:
if th[3] == kwargs.get('db') and th[0] != conn.thread_id():
sql += 'kill %s;' % th[0]
if len(sql.split(';')) > 1:
cursor.execute(sql)
cursor.close()
conn.close()
def connect(self, *args, **kwargs):
''' 创建连接 '''
db = kwargs.get('db')
if db not in self._pool:
size = kwargs.get('size')
if 'size' in kwargs:
kwargs.pop('size')
self._clear_connections(**kwargs)
self._pool[db] = PooledDB(self._db, mincached=size, maxcached=size, *args, **kwargs)
self._connection = self._pool[db].connection()
return ConnectionWrapper(self._connection)
修改mysql目录下的 base.py 文件,在
import MySQLdb as Database
下添加两行
from .pool import DBWrapper
Database = DBWrapper(Database)
修改类 DatabaseWrapper 中的方法 get_connection_params,在
if settings_dict['PORT']:
kwargs['port'] = int(settings_dict['PORT'])
下添加如下代码,使连接池支持"SIZE"参数
if settings_dict.get('SIZE'):
kwargs['size'] = int(settings_dict['SIZE'])
在settings.py配置文件的数据库项中,可以通过配置"SIZE"参数来指定连接池中某数据库的连接数量,如:
DATABASES = {
'default': {
# 'ENGINE': 'django.db.backends.mysql',
'ENGINE': 'database.db.mysql',
'NAME': 'cartoon',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '',
'PORT': '',
# 指定10个连接到cartoon库
'SIZE': '10', # Default '0' means unlimit connection pool size
'OPTIONS': {
'init_command': 'SET default_storage_engine=INNODB',
},
}
}
至此,连接池改造完成。
运行程序,进入mysql命令行,执行
show full processlist;
可以看到已有10个cartoon库的连接