最近遇到一个很蛋疼的问题,写了一个后台管理系统, 由于是后台管理系统,所以使用频率不是很高,当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。至此,世界美好了很多。如果精虫上脑,可以移步至:来撸吧,你会发现世界更美好。

使django与数据库保持长连接的更多相关文章

  1. 让Django支持数据库长连接(可以提高不少性能哦)

    书接上回 上回我们说到:<在生产系统使用Tornado WebServer来代替FastCGI加速你的Django应用> 那么现在很流行用一些高性能的nonblock的app server ...

  2. django 数据库连接模块解析及简单长连接改造

    django 数据库连接模块解析及简单长连接改造工作中纯服务端的项目用到了线程池和django的ORM部分.django 的数据库连接在每一个线程中开启一份,并在查询完毕后自动关闭连接. 线程池处理任 ...

  3. ORM,Django对数据库的连接和使用

    ORM,Django对数据库连接和使用数据 ORM(对象关系映射) 很多语言中的web框架都有这个概念 为什么要有ORM? 写程序离不开数据 新的语法,不需要我们自己写SQL语句 我们按照新的语法写代 ...

  4. http的长连接和短连接(数据库也一样)

    长连接与短连接 所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持. 短连接是指通信双方有数据交互时 ...

  5. django models 数据库操作

    django models 数据库操作 创建模型 实例代码如下 from django.db import models class School(models.Model): pass class ...

  6. (转)HTTP 长连接和短连接

    1. HTTP协议与TCP/IP协议的关系 HTTP的长连接和短连接本质上是TCP长连接和短连接.HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议.IP协议主要解决网络路由和寻址问 ...

  7. HTTP 长连接和短连接

    1. HTTP协议与TCP/IP协议的关系 HTTP的长连接和短连接本质上是TCP长连接和短连接.HTTP属于应用层协议,在传输层使用TCP协议,在网络层使用IP协议.IP协议主要解决网络路由和寻址问 ...

  8. django 操作数据库--orm(object relation mapping)---models

    思想 django为使用一种新的方式,即:关系对象映射(Object Relational Mapping,简称ORM). PHP:activerecord Java:Hibernate C#:Ent ...

  9. Comet:基于 HTTP 长连接的“服务器推”技术

    “服务器推”技术的应用 请访问 Ajax 技术资源中心,这是有关 Ajax 编程模型信息的一站式中心,包括很多文档.教程.论坛.blog.wiki 和新闻.任何 Ajax 的新信息都能在这里找到. c ...

随机推荐

  1. windows下配置启动多个mysql服务

    查找配置做下记录 先安装mysql5.6,安装不在介绍 接下来配置启动另一个mysql服务, 1:先到服务里停止在运行的mysql服务 2:到mysql的安装目录下(默认安装目录在c:\Program ...

  2. [Z] Windows 8/10 audio编程

    都是些网上搜到的比较不错的文章.关于这块儿的内容网上帖子不多.出去下面列的最主要的还有参考MSDN. WASAPI使用介绍: https://blogs.windows.com/buildingapp ...

  3. Auto CAD 2013的故障解决方法

    一.问题的提出 Auto CAD 2013在使用过程中出现了错误:“安全系统(软件锁许可管理器) 不起作用或未正确安装.” 二.问题的分析 网络上很多地方转载了这么一个方法: 1) 启动Windows ...

  4. QWidget::paintEngine: Should no longer be called

    Qt新手,其实并不知道出现这个问题的本质原因,我的问题在于paintEvent中使用的painter是类的成员而不是临时新建的局部变量,改为使用局部变量问题就消失了.

  5. 惊涛怪浪(double dam-break) -- position based fluids

    切入正题之前,先胡说八道几句.    据说爱因斯坦讲过:关于这个世界最难以理解的就是它是可以被理解的.人类在很长的时间里,都无法认知周围变幻莫测的世界,只能编造出无数的神祗来掌控世上万物的运行.到了近 ...

  6. Java知多少(112)数据库之删除记录

    删除数据表也有3种方案 一.使用Statement对象 删除数据表记录的SQL语句的语法是: delete from 表名 where 特定条件 例如 : delete from ksInfo whe ...

  7. xdebug调试一直等待连接

    调试php时一般会启动浏览器,地址栏里一般是 index.php?XDEBUG_SESSION_START=xxx xxx表示调试的ide_key. 开了代理没有关,结果调试时一直无法连上,折腾了好久 ...

  8. java 去掉html标签

    使用正则表达式删除HTML标签. import java.util.regex.Matcher; import java.util.regex.Pattern; public class HTMLSp ...

  9. ionic 实现自动升级APP

    最近做移动商城开发,需要一个自动升级APP的功能,在网上搜罗很多,发现有的是下载APK,有的在我这服务无法实现,由于时间原因,都被PASS了,最后找到了一个热更新插件,经过半天的调试,可以使用,很欣喜 ...

  10. DataTables 入门使用

    前言简述 DataTables是一个JavaScript类库,它操作HTML表格,改变表格CSS样式,增强表格功能使其更具有交互性. DataTables依赖于JQuery类库. 入门示例 环境:Da ...