django mysql长连接_使django与数据库保持长连接

本文介绍了在Django项目中遇到的后台管理系统响应慢的问题,原因是Django每次查询数据库都会新建连接并立即关闭。通过研究源码,了解到可以通过取消请求结束时关闭数据库连接的信号关联来尝试实现长连接,但这种方法在使用fastcgi时无效。最终,通过引入sqlalchemy的pool模块,修改Django的MySQLdb连接方式,实现了数据库的长连接,显著提升了系统性能。
摘要由CSDN通过智能技术生成

最近遇到一个很蛋疼的问题,写了一个后台管理系统, 由于是后台管理系统,所以使用频率不是很高,当django程序在闲置一段时间后,再次打开后台系统,就变得很慢,然后又好了。查了很多方面,从模板引擎到请求(request),再到django配置,nginx等等,都没有查出原因。虽然也查过是不是数据库的原因,但都因为查的不够深入,没有查出所以然。

有一次在处理权限问题时,不断地追朔源代码,由于我使用的是dbroute来控制权限,最后定位到了这个db(具体目录:/usr/local/lib/python2.7/dist-packages/django/db/__init__.py),里面的代码有关于连接的,不看不知道,看了后菊花一紧。我去!原来django每次查询的时候就连一次数据库,而且查询完成之后就立马关掉,真搞不懂作者是怎么想的。每次查询建一次连接不是很耗时间吗?源代码如下:

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)

# `connection`, `DatabaseError` and `IntegrityError` are convenient aliases

# for backend bits.

# DatabaseWrapper.__init__() takes a dictionary, not a settings module, so

# we manually create the dictionary from the settings, passing only the

# settings that the database backends care about. Note that TIME_ZONE is used

# by the PostgreSQL backends.

# We load all these up for backwards compatibility, you should use

# connections['default'] instead.

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'])

# Register an event that closes the database connection

# when a Django request is finished.

def close_connection(**kwargs):

# Avoid circular imports

from django.db import transaction

for conn in connections:

# If an error happens here the connection will be left in broken

# state. Once a good db connection is again available, the

# connection state will be cleaned up.

transaction.abort(conn)

connections[conn].close()

signals.request_finished.connect(close_connection)

# Register an event that resets connection.queries

# when a Django request is started.

def reset_queries(**kwargs):

for conn in connections.all():

conn.queries = []

signals.request_started.connect(reset_queries)

# Register an event that rolls back the connections

# when a Django request has an exception.

def _rollback_on_exception(**kwargs):

from django.db import transaction

for conn in connections:

try:

transaction.rollback_unless_managed(using=conn)

except DatabaseError:

pass

signals.got_request_exception.connect(_rollback_on_exception)

发现它是接收到了一个关闭信号后就立马关闭,其实也就是每一次django请求,它就连接一次和查询一次。网上查了下资料,有个人是这样解决的:

在django项目中的__init__.py文件中加入如下代码来实现:

from django.core import signals

from django.db import close_connection

# 取消信号关联,实现数据库长连接

signals.request_finished.disconnect(close_connection)

它采用的原理是最后一行代码相关的Signal对象,其中还有一个disconnect方法,对应实现取消信号关联。

我照着他的实现来做,发现不行,在闲置几小时后,打开后台依然需要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/db/backends/mysql目录下的文件全部拷贝出来,放在项目的一个libs/mysql下面,然后修改base.py文件。找到

try:

import MySQLdb as Database

except ImportError as e:

from django.core.exceptions import ImproperlyConfigured

raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)

这段代码,在下面添加:

from sqlalchemy import pool

Database = pool.manage(Database)

基本上就可以了。如果还想再修改,就找到

self.connection = Database.connect(**kwargs)

把它注释掉,添加

self.connection = Database.connect(

host=kwargs.get('host', '127.0.0.1'),

port=kwargs.get('port', 3306),

user=kwargs['user'],

db=kwargs['db'],

passwd=kwargs['passwd'],

use_unicode=kwargs['use_unicode'],

charset='utf8'

)

再修改一下settings.py的数据库配置,把django.db.backends.mysql改为libs.mysql。至此,世界美好了很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值