最近遇到一個很蛋疼的問題,寫了一個後臺管理系統, 因爲是後臺管理系統,因此使用頻率不是很高,當django程序在閒置一段時間後,再次打開後臺系統,就變得很慢,而後又好了。查了不少方面,從模板引擎到請求(request),再到django配置,nginx等等,都沒有查出緣由。雖然也查過是否是數據庫的緣由,但都由於查的不夠深刻,沒有查出因此然。html
有一次在處理權限問題時,不斷地追朔源代碼,因爲我使用的是dbroute來控制權限,最後定位到了這個db(具體目錄:/usr/local/lib/python2.7/dist-packages/django/db/__init__.py),裏面的代碼有關於鏈接的,不看不知道,看了後菊花一緊。我去!原來django每次查詢的時候就連一次數據庫,並且查詢完成以後就立馬關掉,真搞不懂做者是怎麼想的。每次查詢建一次鏈接不是很耗時間嗎?源代碼以下:python
-
from django.conf import settings
-
from django.core import signals
-
from django.core.exceptions import ImproperlyConfigured
-
from django.db.utils import (ConnectionHandler, ConnectionRouter,
-
load_backend, DEFAULT_DB_ALIAS, DatabaseError, IntegrityError)
-
-
__all__ = (
'backend', 'connection', 'connections', 'router', 'DatabaseError',
-
'IntegrityError', 'DEFAULT_DB_ALIAS')
-
-
-
if settings.DATABASES and DEFAULT_DB_ALIAS not in settings.DATABASES:
-
raise ImproperlyConfigured("You must define a '%s' database" % DEFAULT_DB_ALIAS)
-
-
connections = ConnectionHandler(settings.DATABASES)
-
-
router = ConnectionRouter(settings.DATABASE_ROUTERS)
-
-
-
-
-
-
-
-
-
-
-
class DefaultConnectionProxy(object):
-
-
Proxy for accessing the default DatabaseWrapper object's attributes. If you
-
need to access the DatabaseWrapper object itself, use
-
connections[DEFAULT_DB_ALIAS] instead.
-
-
def __getattr__(self, item):
-
return getattr(connections[DEFAULT_DB_ALIAS], item)
-
-
def __setattr__(self, name, value):
-
return setattr(connections[DEFAULT_DB_ALIAS], name, value)
-
-
connection = DefaultConnectionProxy()
-
backend = load_backend(connection.settings_dict[
'ENGINE'])
-
-
-
-
def close_connection(**kwargs):
-
-
from django.db import transaction
-
-
-
-
-
-
connections[conn].close()
-
signals.request_finished.connect(close_connection)
-
-
-
-
def reset_queries(**kwargs):
-
for conn in connections.all():
-
-
signals.request_started.connect(reset_queries)
-
-
-
-
def _rollback_on_exception(**kwargs):
-
from django.db import transaction
-
-
-
transaction.rollback_unless_managed(using=conn)
-
-
-
signals.got_request_exception.connect(_rollback_on_exception)
發現它是接收到了一個關閉信號後就立馬關閉,其實也就是每一次django請求,它就鏈接一次和查詢一次。網上查了下資料,有我的是這樣解決的:mysql
在django項目中的__init__.py文件中加入以下代碼來實現:nginx
-
from django.core import signals
-
from django.db import close_connection
-
-
-
signals.request_finished.disconnect(close_connection)
它採用的原理是最後一行代碼相關的Signal對象,其中還有一個disconnect方法,對應實現取消信號關聯。sql
我照着他的實現來作,發現不行,在閒置幾小時後,打開後臺依然須要10秒左右。因爲我使用的是fastcgi來運行django,可能django在沒有動靜的時候,fastcgi就把它掛起,因此就還會去從新創建鏈接。若是使用supervisord來運行,估計不會。數據庫
既然這個方法不行,那就再看一下它的源碼,發現backends目錄下面有mysql,oracle,postgresql_psycopg2,sqlite3等,因而選擇msql進去看一下base.py文件。發現django是直接封裝MySQLdb,每建立一個MySQLdb對象其實也就進行了一次鏈接。能不能像線程池同樣,一次性建立多個MySQLdb對象呢?答案是確定的。sqlalchemy有個pool可讓數據庫保持長鏈接,那就直接把這個文件裏的Database改爲sqlalchemy的pool。固然,咱們不能暴力地修改去修改源碼。由於這個模塊是用來創建數據庫鏈接的,因此能夠獨立出來。其實很簡單,只須要修改base.py 文件幾處就行。實現方法以下:django
把django/db/backends/mysql目錄下的文件所有拷貝出來,放在項目的一個libs/mysql下面,而後修改base.py文件。找到oracle
try:
import MySQLdb as Database
except ImportError as e:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
這段代碼,在下面添加:app
-
from sqlalchemy import pool
-
Database = pool.manage(Database)
基本上就能夠了。若是還想再修改,就找到less
self.connection = Database.connect(**kwargs)
把它註釋掉,添加
-
self.connection = Database.connect(
-
host=kwargs.get('host', '127.0.0.1'),
-
port=kwargs.get('port', 3306),
-
-
-
-
use_unicode=kwargs['use_unicode'],
-
-
再修改一下settings.py的數據庫配置,把django.db.backends.mysql改成libs.mysql。至此,世界美好了不少。若是精蟲上腦,能夠移步至:來擼吧,你會發現世界更美好。