Python可以使用MySQLdb进行数据库操作,但是每次连接MySQL数据库请求时,都是独立的去请求访问,相当浪费资源,而且访问数量达到一定数量时,对mysql的性能会产生较大的影响。实际使用中,通常会使用数据库的连接池来访问数据库达到资源复用的目的。这里介绍一下在django中如何实现数据库连接池。
DBUtils
DBUtils是一套python数据库连接池包,并允许对非线程安全的数据库接口进行线程安全包装。DBUtils来自Webware for Python。
下载地址:DBUtils
DBUtils提供两种外部接口:
- PersistentDB :提供线程专用的数据库连接,并自动管理连接。
- PooledDB :提供线程间可共享的数据库连接,并自动管理连接。
将下载下来的源码解压,放在应用程序根目录”database”中,修改SteadyDB.py文件,在类 SteadyDBConnection 中添加3个成员方法:1
2
3
4
5
6
7
8
9def autocommit(self, *args, **kwargs):
self._con.autocommit(*args, **kwargs)
def get_server_info(self):
return self._con.get_server_info()
def encoders(self):
return self._con.encoders
修改 PooledDB.py 和 PersistentDB.py 两个文件,将1
from DBUtils.SteadyDB import connect
改为:1
from .SteadyDB import connect
将Django中的mysql模块加入应用程序目录中,将backends目录下的 __init__.py文件和mysql目录复制到database目录下的db目录中,目录结构如下:1
2
3
4
5
6
7
8
9应用程序根目录
|
| -- database
|
| -- DBUtils
| -- db
|
| -- __init__.py
| -- mysql
在mysql目录下添加pool.py文件,如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52# 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 文件,在1
import MySQLdb as Database
下添加两行1
2from .pool import DBWrapper
Database = DBWrapper(Database)
修改类 DatabaseWrapper 中的方法 get_connection_params,在1
2if settings_dict['PORT']:
kwargs['port'] = int(settings_dict['PORT'])
下添加如下代码,使连接池支持”SIZE”参数1
2if settings_dict.get('SIZE'):
kwargs['size'] = int(settings_dict['SIZE'])
在settings.py配置文件的数据库项中,可以通过配置”SIZE”参数来指定连接池中某数据库的连接数量,如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16DATABASES = {
'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命令行,执行1
show full processlist;
可以看到连接池里面所有的连接。
[^1]: 参考链接: Django实现MySQL连接池.