最近遇到一个很蛋疼的问题,写了一个后台管理系统, 由于是后台管理系统,所以使用频率不是很高,当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. unity web player的debug和log信息

    win8模式下 unity web player的报错信息在如下目录下:C:\Users\xxx\AppData\Local\Temp\UnityWebPlayer\log 注:目录里的文件可能被隐藏 ...

  2. QT编写DLL给外部程序调用,提供VC/C#/C调用示例(含事件)

    最近这阵子,接了个私活,封装一个开发包俗称的SDK给客户调用,查阅了很多人家的SDK,绝大部分用VC编写,而且VC6.0居多,估计也是为了兼容大量的XP用户及IE浏览器,XP自带了VC6.0运行库,所 ...

  3. Android不规则点击区域详解

    Android不规则点击区域详解 摘要 今天要和大家分享的是Android不规则点击区域,准确说是在视觉上不规则的图像点击响应区域分发. 其实这个问题比较简单,对于很多人来说根本不值得做为一篇博文写出 ...

  4. java数据库连接池性能对比

    这个测试的目的是验证当前常用数据库连接池的性能. testcase Connection conn = dataSource.getConnection(); PreparedStatement st ...

  5. 【转】ORATOP工具使用说明

    文章转自:http://lintzyuan.blogspot.jp/2014/07/oratop.html ORATOP   前言:隨著PC Server的規格及速度愈來愈快,大多數的公司摒棄大型主機 ...

  6. jQuery手机端上拉刷新下拉加载更多页面

    基于jQuery手机端上拉下拉刷新页面代码.这是一款类似QQ空间客户端或者微信下拉刷新页面特效代码.效果图如下: 在线预览   源码下载 实现的代码. html代码: <div id=" ...

  7. imagepng或imagejpeg浏览器无显示问题

    可以先参考这篇文章,检查一下php的文件编码是否有bom 然而我并不是这个问题,后来想到或许是输出缓冲中有其它内容, 于是尝试 ob_clean();$imagepng($im);或//imagejp ...

  8. 斐波那契堆(三)之 Java的实现

    概要 前面分别通过C和C++实现了斐波那契堆,本章给出斐波那契堆的Java版本.还是那句老话,三种实现的原理一样,择其一了解即可. 目录1. 斐波那契堆的介绍2. 斐波那契堆的基本操作3. 斐波那契堆 ...

  9. Appium移动自动化测试(二)--安装Android开发环境

    继续Appium环境的搭建. 第二节  安装Android开发环境 如果你的环境是MAC那么可以直接跳过这一节.就像我们在用Selenium进行web自动化测试的时候一样,我们需要一个浏览器来执行测试 ...

  10. Webstrom (或Phpstrom)使用git(oschina-码云)

      .登录"码云"(题外话,这名字起得真好),创建一个新项目   .自动进入了新项目主页,复制该git 仓库的https地址,第4步会用到   .打开Webstrom,选择chec ...