Django持久数据库连接

时间:2009-07-14 13:47:01

标签: python database django mod-wsgi persistent

我正在使用django和apache以及mod_wsgi和PostgreSQL(都在同一主机上),我需要处理很多简单的动态页面请求(每秒数百个)。我遇到的问题是瓶颈是django没有持久的数据库连接并且在每个请求上重新连接(需要接近5ms)。 在进行基准测试时,我得到了持久连接,我可以在500 r / s附近处理,而没有我只获得50 r / s。

有人有什么建议吗?如何修改django以使用持久连接?或者加速从python到DB的连接

提前致谢。

7 个答案:

答案 0 :(得分:28)

Django 1.6 添加了persistent connections support (link to doc for django 1.9)

  

持久连接避免了重新建立连接的开销   每个请求中与数据库的连接。他们受到了控制   CONN_MAX_AGE参数,用于定义a的最大生存期   连接。它可以为每个数据库单独设置。

答案 1 :(得分:22)

尝试PgBouncer - PostgreSQL的轻量级连接池。 特性:

  • 旋转连接时的几个级别的暴行:
    • 会话池
    • 交易池
    • 声明合并
  • 内存要求低(默认情况下每个连接2k)。

答案 2 :(得分:20)

在Django主干中,编辑django/db/__init__.py并注释掉该行:

signals.request_finished.connect(close_connection)

此信号处理程序使其在每次请求后都与数据库断开连接。我不知道这样做的所有副作用是什么,但是在每个请求之后启动新连接没有任何意义;正如你所注意到的,它会摧毁你的表现。

我现在正在使用它,但我没有做过全套测试,看看是否有任何问题。

我不知道为什么每个人都认为这需要新的后端或特殊的连接池或其他复杂的解决方案。这似乎很简单,虽然我不怀疑有一些模糊不清的问题让他们首先这样做 - 应该更明智地处理;正如您所注意到的,对于高性能服务而言,每个请求的5ms开销是相当多的。 (这需要我 150ms - 我还没弄明白为什么。)

编辑:另一个必要的变化是在django / middleware / transaction.py;删除两个transaction.is_dirty()测试并始终调用commit()或rollback()。否则,如果它只从数据库中读取,它将不会提交事务,这将保持应该关闭的锁定。

答案 3 :(得分:15)

我创建了一个小Django patch,通过sqlalchemy池实现MySQL和PostgreSQL的连接池。

这完全适用于http://grandcapital.net/长时间的制作。

补丁是在搜索了一下主题之后编写的。

答案 4 :(得分:3)

免责声明:我没试过。

我相信您需要实现自定义数据库后端。 Web上有一些示例说明了如何使用连接池实现数据库后端。

使用连接池可能是一个很好的解决方案,因为当连接返回到池时,网络连接保持打开状态。

  • This post通过修补Django来实现这一点(其中一条评论指出在核心django代码之外实现自定义后端更好)
  • This post是自定义数据库后端的实现

两个帖子都使用MySQL - 也许你可以在Postgresql中使用类似的技术。

修改

  • Django Book使用pgpooltutorial)提及Postgresql连接池。
  • 有人发布了a patch来获取实现连接池的psycopg2后端。我建议在您自己的项目中创建现有后端的副本并修补该后端。

答案 5 :(得分:0)

我做了一些小的自定义psycopg2后端,它使用全局变量实现持久连接。 有了这个,我能够将每秒请求的数量从350提高到1600(非常简单的页面,选择很少) 只需将其保存在任何目录中名为base.py的文件中(例如postgresql_psycopg2_persistent)并在设置中设置

DATABASE_ENGINE到projectname.postgresql_psycopg2_persistent

注意!!!代码不是线程安全的 - 你不能将它与python线程一起使用,因为结果是无法预料的,​​如果是mod_wsgi,请使用prefork守护进程模式,其中threads = 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

或者这是一个线程安全的,但是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

答案 6 :(得分:0)

这是django连接池的一个包: django-db-connection-pool

pip install django-db-connection-pool

您可以提供额外的选项来传递给 SQLAlchemy 的池创建,键的名称是 POOL_OPTIONS:

DATABASES = {
    'default': {
        ...
        'POOL_OPTIONS' : {
            'POOL_SIZE': 10,
            'MAX_OVERFLOW': 10
        }
        ...
     }
 }