Django持久数据库连接
-
13-09-2019 - |
题
我将 django 与 apache、mod_wsgi 和 PostgreSQL(都在同一主机上)一起使用,并且我需要处理大量简单的动态页面请求(每秒数百个)。我面临的问题是,瓶颈是 django 没有持久的数据库连接,并且在每个请求上重新连接(需要近 5 毫秒)。在进行基准测试时,我发现使用持久连接我可以处理近 500 r/s,而没有持久连接我只能处理 50 r/s。
有人有什么建议吗?如何修改django使用持久连接?或者加速Python到DB的连接
提前致谢。
解决方案
的Django的 1.6 强>已加入持久连接支持(链接到文档为django的1.9):
持久连接避免的重新建立的开销 连接到每个请求的数据库。他们通过控制 CONN_MAX_AGE参数限定的最大生存期 连接。它可以独立地为每个数据库进行设定。
其他提示
尝试 保镖 - PostgreSQL 的轻量级连接池。特征:
- 旋转连接时的几种残酷程度:
- 会话池
- 交易池
- 语句池
- 内存要求低(默认情况下每个连接 2k)。
在Django的躯干,编辑django/db/__init__.py
并注释掉行:
signals.request_finished.connect(close_connection)
此信号处理程序导致其从每个请求之后数据库断开连接。我不知道所有的这样做将是副作用,但它没有任何意义,开始每次请求后一个新的连接;它破坏性能,因为你已经注意到了。
我现在用这个,但我没有带做了全套的测试,看看是否有任何中断。
我不知道为什么每个人都认为这需要一个新的后端或特殊的连接池或者其他复杂的解决方案。这似乎很简单,但我不怀疑有一些让他们在第一时间做一些不起眼的陷阱 - 这应该更理智地处理; 5ms的开销为每个请求是相当多了一个高性能的服务,因为你已经注意到了。 (这需要我的 150ms的的 - 我没有带想通了,为什么还)
编辑:另一个必要的变化是在Django /中间件/ transaction.py;拆下两个transaction.is_dirty()测试,并始终调用commit()或rollback()。否则,将不提交事务,如果它只是从数据库中,这将离开锁打开,应封闭阅读。
我创建的经由SQLAlchemy的池实现MySQL和PostgreSQL的连接池小 Django的补丁
此工作完全生产 http://grandcapital.net/ 的一段长的时间。
在贴剂使用Google该主题的位后写入。
免责声明:我没试过这个。
我相信您需要实现一个自定义数据库后端。网络上有一些示例展示了如何使用连接池实现数据库后端。
对于您的情况,使用连接池可能是一个很好的解决方案,因为当连接返回到池时,网络连接保持打开状态。
这两篇文章都使用 MySQL - 也许您可以对 Postgresql 使用类似的技术。
编辑:
我提出,使用全局变量实现持久连接一些小的自定义psycopg2后端。
有了这个,我能提高每秒请求数从350到1600的大写金额(除了少数选择很简单页)
只是它保存在文件名为base.py
在任何目录中(例如postgresql_psycopg2_persistent),并在设置中设置
DATABASE_ENGINE到projectname.postgresql_psycopg2_persistent
注意!!!代码不是线程 - 不能与由于不可预料的结果蟒线程使用它,在mod_wsgi的的情况下,请使用带有螺纹的prefork daemon模式= 1 强>
# 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
或者这里是一个线程安全的,但蟒蛇线程不使用多个内核,这样你就不会得到这样的性能提升与前一个。您可以使用这一个多过程中的一个了。
# 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