注:筆者的Django版本為 1.9,Python版本為 2.7
一直想在Django中實(shí)現(xiàn)MySQL連接池帕识,在網(wǎng)上尋找良久無果,偶然之間晶姊,看到了一個(gè)帖子"讓Django支持?jǐn)?shù)據(jù)庫長連接(可以提高不少性能哦)"伪货,受到啟發(fā),既然可以實(shí)現(xiàn)長連接蒙挑,那么也可以實(shí)現(xiàn)連接池了愚臀,動(dòng)手姑裂!
原理與鏈接帖子是一樣的,只是將數(shù)據(jù)庫的連接實(shí)例改為連接池的連接實(shí)例炭分。連接池使用DBUtils實(shí)現(xiàn)捧毛,但在Django中让网,需要對DBUtils進(jìn)行一點(diǎn)小改動(dòng)。
一部分內(nèi)容是與鏈接帖子一樣的而账,在此為了完整性因篇,只做一次搬運(yùn)工。
第一步吹缔,對DBUtils動(dòng)個(gè)小手術(shù)锯茄。
DBUtils是一個(gè)實(shí)現(xiàn)數(shù)據(jù)庫連接池的庫肌幽,支持DB-API 2接口規(guī)范,點(diǎn)擊下載。解壓下載后的壓縮包讥蔽,將其中的DBUtils目錄加入應(yīng)用程序目錄中冶伞,筆者將其放在應(yīng)用程序根目錄"database"中步氏,Docs荚醒、Examples侯繁、Tests這3個(gè)目錄可以刪除贮竟,修改SteadyDB.py文件较剃,在類 SteadyDBConnection 中添加3個(gè)成員方法写穴,如下:
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 兩個(gè)文件啊送,將
from DBUtils.SteadyDB import connect
改為
from .SteadyDB import connect
第二步欣孤,"貍貓換太子"翔冀。
將Django中的mysql模塊加入應(yīng)用程序目錄中,筆者將Django安裝在虛擬環(huán)境中搬瑰,位置是 Django虛擬環(huán)境路徑/lib/python2.7/site-packages/django/db/backends控硼,將backends目錄下的 __init__.py文件和mysql目錄復(fù)制到database目錄下的db目錄中,目錄結(jié)構(gòu)如下:
應(yīng)用程序根目錄
|
| -- 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):
''' 代理數(shù)據(jù)庫連接的屬性方法 '''
return getattr(self._conn, method)
def close(self):
''' 代理Django的關(guān)閉數(shù)據(jù)庫連接 '''
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):
''' 關(guān)閉已有連接 '''
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):
''' 創(chuàng)建連接 '''
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"參數(shù)
if settings_dict.get('SIZE'):
kwargs['size'] = int(settings_dict['SIZE'])
在settings.py配置文件的數(shù)據(jù)庫項(xiàng)中,可以通過配置"SIZE"參數(shù)來指定連接池中某數(shù)據(jù)庫的連接數(shù)量肩狂,如:
DATABASES = {
'default': {
# 'ENGINE': 'django.db.backends.mysql',
'ENGINE': 'database.db.mysql',
'NAME': 'cartoon',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '',
'PORT': '',
# 指定10個(gè)連接到cartoon庫
'SIZE': '10', # Default '0' means unlimit connection pool size
'OPTIONS': {
'init_command': 'SET default_storage_engine=INNODB',
},
}
}
至此姥饰,連接池改造完成列粪。
運(yùn)行程序,進(jìn)入mysql命令行篱竭,執(zhí)行
show full processlist;
可以看到已有10個(gè)cartoon庫的連接