本篇文章主要介绍了"Django与MySQL数据库长连接问题",主要涉及到方面的内容,对于MySql感兴趣的同学可以参考一下:
前段时间由于项目要求,需要在Django 1.4.3版本上实现跟MySQL数据库的长连接,因为是刚开始接触Django框架,对Django的源码也不是很熟,只能...
前段时间由于项目要求,需要在Django 1.4.3版本上实现跟MySQL数据库的长连接,因为是刚开始接触Django框架,对Django的源码也不是很熟,只能参考着网上的解决方案来一点点摸索,在此记录下解决过程中发现的一些问题和心得吧!
百度“Django 数据库长连接”主要能搜到三种解决方案,我参考这三种解决方案来实现的时候,发现都不能很好的解决Django跟MySQL数据库的长连接问题,不知道是我对这些解决方案理解有误,还是真的存在问题?在此列出我对这三种解决方案认为不合理的地方和我最终的解决方案吧!
网上方案一:
不关闭Django每次request中创建的DB connection,具体实现方案请参考:握瑜的专栏-Django实现数据库长连接
这种方案我测试的时候发现connection确实实现了长连接,但每次Django request的时候都会创建一个connection,其实下次request使用的connection是没有复用上次request的connection的,而是使用新创建的connection,随着request的增多,connection会越来越多,最终达到数据库连接的上限,这样实现的长连接就没有多大意义了。
网上方案二:
自己实现连接池管理,具体实现方案请参考:懒人居-让Django支持数据库长连接(可以提高不少性能哦)
这种方案我没有详细的看他实现的代码,大概了解应该是自己编码实现了一个类似pool来管理connection,我就直接按照上面的方案和源码拷贝到我的项目里测试了下,发现有报错,具体什么错误我没有详细推敲!各位大神可以尝试下!
网上方案三:
使用第三方pool来管理connection,具体实现方案请参考:李少麒-使django与数据库保持长连接
这个实现方案使用第三方python库sqlalchemy中的pool来管理Django与DB的长连接,其实按照他的实现方案已经基本能够实现长连接了,但我觉得还是有两个不足或者说值得改进的地方,改进之后也就是我最后实现的方案。
不足一:
文中将Django跟MySQL的连接调用参数简化了,即将django/db/backends/mysql/base.py中
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'
)
这样改之后确实能实现长连接了,
sqlalchemy的pool也不会报错了(对,如果不这样改,直接按原来的kwargs参数调用连接的话pool会报错,我不知道作者是不是因为pool报错而把参数改到下面这种方式的。),但是其实这样参数简化后,将原来kwargs中本应该传递进去的部分参数过滤掉了,我不知道会不会影响其他功能(那部分参数的作用我没具体深入源码去了解,希望后续在熟悉Django源码的过程中再深入了解吧)。为了不至于引入其他问题,我还是决定按原来的方式直接将kwargs整个传入,刚说了,整体kwargs传入的话,pool会报unhashable的一个错,那是因为kwargs中有个key(conv)对应的value(django_conversions)是个字典(见如下代码段),而在sqlalchemy实现的pool中,会把Database.connection()函数中传入的参数名和值以(key,value)组成元组作为新的key保存在pool中,用来管理连接池,但是因为django_conversions这个value是个字典,python中字典是不允许出现中key中的,就导致了pool报unhashable的错误!
kwargs = {
'conv': django_conversions,
'charset': 'utf8',
'use_unicode': True,
}
那么如何解决?说说我的解决方法(个人认为不是很好,但我一时也想不出好的方法,就这么用了),我修改了sqlalchemy中pool的实现源码(sqlalchemy/pool.py文件最后一个函数),原来:
def _serialize(self, *args, **kw):
if "sa_pool_key" in kw:
return kw['sa_pool_key']
return tuple(
list(args) +
[(k, kw[k]) for k in sorted(kw)]
修改后:
def _serialize(self, *args, **kw):
if "sa_pool_key" in kw:
return kw['sa_pool_key']
kw_list = []
for k in sorted(kw):
if isinstance(kw[k], dict) or isinstance(kw[k], list):
continue
kw_list.append((k, kw[k]))
return tuple(list(args) + kw_list)
也就是把可能导致pool报unhashable的参数过滤掉,不作为pool的key,这样我通过读pool的源码实现是不会影响pool的管理的,同时真正去connection是还是按全量的kwargs实现连接的。
不足二:
文中没有考虑MySQL数据库wait_timeout的设置,默认情况下MySQL的wait_timeout是28800s(即8个小时),也就是说如果Django跟MySQL的长连接空闲等待时间超过8个小时的话,MySQL会主动断开跟Django的连接,如果断开连接后,有新的request需要连接数据库的话,会报"MySQL
server has gone away"的错误。
怎么解决?其实sqlalchemy的pool在创建的时候可以传入recycle参数来实现长连接超过一定时间后的重连,具体修改如下,将方案三种中对django/db/backends/mysql/base.py最开始的修改:
from sqlalchemy import pool
Database = pool.manage(Database)
改为:
from sqlalchemy import pool
Database = pool.manage(Database, **{"recycle": 25200})
其中recycle时间(25200)可以自己根据MySQL的wait_timeout配置进行修改!
好了,解决这两个不足之后,我觉得基本可以实现Django 1.4.3跟MySQL数据库的长连接了!
不足之处欢迎各位大神指点!
听说Django 1.6之后已经有参数可以配置长连接了,这个后面再去深入了解下!哈哈!
以上就介绍了Django与MySQL数据库长连接问题,包括了方面的内容,希望对MySql有兴趣的朋友有所帮助。
本文网址链接:http://www.codes51.com/article/detail_136034.html