Django持久数据库连接
我用Apache和mod_wsgi和PostgreSQL的django(都在同一主机上),我需要处理很多简单的dynamic页面请求(每秒数百)。 我面临的问题是,Django没有持久的数据库连接,并重新连接每个请求(大约需要5ms)的瓶颈。 在做基准testing时,我得到了持续的连接,我可以处理接近500 r / s的速度,而没有得到50 r / s的速度。
任何人有任何build议? 如何修改Django使用持久连接? 或者加速从python到数据库的连接
提前致谢。
Django 1.6添加了持久连接支持(链接到django 1.9的doc) :
持久连接避免了在每个请求中重新build立与数据库的连接的开销。 它们由CONN_MAX_AGE参数控制,该参数定义连接的最长生存期。 它可以为每个数据库独立设置。
尝试PgBouncer – 一个PostgreSQL的轻量级连接池。 特征:
- 旋转连接时的几个级别的残酷:
- 会话池
- 事务池
- 语句池
- 内存要求低(默认每个连接2k)。
在Django主干中,编辑django/db/__init__.py
并注释掉该行:
signals.request_finished.connect(close_connection)
这个信号处理程序导致它在每次请求之后从数据库断开连接。 我不知道这样做的所有副作用是什么,但在每次请求之后开始一个新的连接没有任何意义; 它会破坏性能,正如你注意到的那样。
我现在正在使用这个,但是我没有做一套全面的testing来看看是否有什么问题。
我不知道为什么每个人都认为这需要一个新的后端或一个特殊的连接池或其他复杂的解决scheme。 这似乎很简单,但我不怀疑有一些模糊的陷阱,使他们首先这样做 – 应该更明智地处理; 您已经注意到,每个请求的5ms开销对于高性能服务来说相当重要。 (这需要我150毫秒 – 我还没有弄清楚为什么。)
编辑:另一个必要的改变是在django / middleware / transaction.py; 删除两个transaction.is_dirty()testing,并始终调用commit()或rollback()。 否则,如果仅从数据库中读取数据,则不会提交事务,这将使应该closures的锁打开。
我创build了一个小的Django补丁 ,通过sqlalchemy池实现MySQL和PostgreSQL的连接池。
这对长期的http://grandcapital.net/的生产是完美的。;
这个补丁是在Google上search这个主题后写的。
免责声明:我没有试过这个。
我相信你需要实现一个自定义数据库后端。 网上有几个例子说明了如何用连接池实现数据库后端。
使用连接池可能会是一个很好的解决scheme,因为连接返回到池时networking连接保持打开状态。
- 这篇文章通过修补Django来完成这个任务(其中一个注释指出,最好在核心django代码之外实现一个自定义的后端)
- 这篇文章是一个自定义数据库后端的实现
这两个post使用MySQL – 也许你可以使用Postgresql类似的技术。
编辑:
- Django Book提到了使用pgpool ( tutorial )的Postgresql连接池。
- 有人为psycopg2后端发布了一个补丁,用于实现连接池。 我build议在自己的项目中创build一个现有后端的副本并修补它。
我做了一些使用全局variables实现持久连接的小型自定义psycopg2后端。 有了这个,我能够改进从350到1600每秒的请求数量(在很lessselect的非常简单的页面上)只需将其保存在任何目录下的base.py
文件(例如postgresql_psycopg2_persistent)中并在设置中设置
DATABASE_ENGINE到projectname.postgresql_psycopg2_persistent
注意!!! 该代码不是线程安全的 – 你不能用python线程,因为意想不到的结果,在mod_wsgi的情况下,请使用线程= 1的prefork守护进程模式
# Custom DB backend postgresql_psycopg2 based # implements persistent database connection using global variable from django.db.backends.postgresql_psycopg2.base import DatabaseError, DatabaseWrapper as BaseDatabaseWrapper, \ IntegrityError from psycopg2 import OperationalError connection = None class DatabaseWrapper(BaseDatabaseWrapper): def _cursor(self, *args, **kwargs): global connection if connection is not None and self.connection is None: try: # Check if connection is alive connection.cursor().execute('SELECT 1') except OperationalError: # The connection is not working, need reconnect connection = None else: self.connection = connection cursor = super(DatabaseWrapper, self)._cursor(*args, **kwargs) if connection is None and self.connection is not None: connection = self.connection return cursor def close(self): if self.connection is not None: self.connection.commit() self.connection = None
或者这是一个线程安全的,但是python线程不使用多核,所以你不会像以前那样提高性能。 你也可以使用这个多进程的一个。
# Custom DB backend postgresql_psycopg2 based # implements persistent database connection using thread local storage from threading import local from django.db.backends.postgresql_psycopg2.base import DatabaseError, \ DatabaseWrapper as BaseDatabaseWrapper, IntegrityError from psycopg2 import OperationalError threadlocal = local() class DatabaseWrapper(BaseDatabaseWrapper): def _cursor(self, *args, **kwargs): if hasattr(threadlocal, 'connection') and threadlocal.connection is \ not None and self.connection is None: try: # Check if connection is alive threadlocal.connection.cursor().execute('SELECT 1') except OperationalError: # The connection is not working, need reconnect threadlocal.connection = None else: self.connection = threadlocal.connection cursor = super(DatabaseWrapper, self)._cursor(*args, **kwargs) if (not hasattr(threadlocal, 'connection') or threadlocal.connection \ is None) and self.connection is not None: threadlocal.connection = self.connection return cursor def close(self): if self.connection is not None: self.connection.commit() self.connection = None