代码之家  ›  专栏  ›  技术社区  ›  HardQuestions

Django持久数据库连接

  •  53
  • HardQuestions  · 技术社区  · 16 年前

    我将django与apache、mod_wsgi和postgresql一起使用(都在同一主机上),我需要处理许多简单的动态页面请求(每秒数百个)。我面临的问题是,瓶颈在于django没有持久的数据库连接,并且在每个请求上重新连接(这需要大约5毫秒)。 在做基准测试时,我发现通过持久连接,我可以处理接近500 r/s的流量,而如果没有,我只能处理50 r/s的流量。

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

    事先谢谢。

    6 回复  |  直到 9 年前
        1
  •  28
  •   qwetty    9 年前

    丹戈 一点六 增加了 persistent connections support (link to doc for django 1.9) :

    持久连接避免了重新建立 在每个请求中连接到数据库。它们由 conn_max_age参数,用于定义 连接。它可以为每个数据库单独设置。

        2
  •  22
  •   Cristian Ciupitu    11 年前

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

    • 旋转连接时的几种残忍程度:
      • 会话池
      • 事务池
      • 语句池
    • 内存需求低(默认情况下,每个连接2公里)。
        3
  •  20
  •   Glenn Maynard    16 年前

    在Django主干中,编辑 django/db/__init__.py 并对这一行进行评论:

    signals.request_finished.connect(close_connection)
    

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

    我现在正在使用这个,但是我还没有做一整套测试来检查是否有任何问题。

    我不知道为什么每个人都认为这需要一个新的后端或者一个特殊的连接池或者其他复杂的解决方案。这看起来很简单,尽管我不怀疑有一些模糊的gotcha让他们首先这样做——应该更明智地处理;正如您注意到的,对于高性能服务来说,每个请求的5毫秒开销是相当大的。(需要我 150毫秒 --我还不知道为什么。)

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

        4
  •  15
  •   Igor Katson    14 年前

    我创造了一个小 Django patch 通过sqlachemy pooling实现mysql和postgresql的连接池。

    这对生产 http://grandcapital.net/ 很长一段时间。

    这个补丁是在谷歌搜索这个话题之后写的。

        5
  •  3
  •   codeape    16 年前

    免责声明:我没有尝试过。

    我认为您需要实现自定义数据库后端。在Web上有几个例子展示了如何使用连接池实现数据库后端。

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

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

    这两篇文章都使用MySQL——也许你可以在PostgreSQL中使用类似的技术。

    编辑:

    • Django书提到PostgreSQL连接池,使用 pgpool ( tutorial )
    • 有人张贴 a patch 用于实现连接池的psycopg2后端。我建议在您自己的项目中创建一个现有后端的副本并修补它。
        6
  •  0
  •   HardQuestions    15 年前

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

    数据库引擎到projectname.postgresql_psycopg2_persistent

    注意!!!!代码不是threadsafe-您不能将它与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