Постоянное соединение с базой данных Django - PullRequest
54 голосов
/ 14 июля 2009

Я использую django с apache и mod_wsgi и PostgreSQL (все на одном хосте), и мне нужно обрабатывать множество простых динамических запросов страниц (сотни в секунду). Я столкнулся с проблемой, заключающейся в том, что узким местом является то, что у django нет постоянного подключения к базе данных, и он переподключается при каждом запросе (это занимает около 5 мс). При выполнении теста я понял, что при постоянном соединении я могу справиться со скоростью около 500 об / с, а без этого я получаю только 50 об / с.

У кого-нибудь есть совет? Как изменить django, чтобы использовать постоянное соединение? Или ускорить подключение от питона к БД

Заранее спасибо.

Ответы [ 6 ]

28 голосов
/ 18 октября 2013

Django 1.6 добавил поддержка постоянных соединений (ссылка на doc для django 1.9) :

Постоянные соединения позволяют избежать затрат на восстановление подключение к базе данных в каждом запросе. Они контролируются Параметр CONN_MAX_AGE, который определяет максимальное время жизни подключение. Он может быть установлен независимо для каждой базы данных.

22 голосов
/ 09 ноября 2009

Попробуйте PgBouncer - облегченный пул соединений для PostgreSQL. Особенности:

  • Несколько уровней жестокости при вращении соединений:
    • Пул сессий
    • Пул транзакций
    • Объединение операторов
  • Низкие требования к памяти (по умолчанию 2 КБ на соединение).
20 голосов
/ 24 июля 2009

В стволе Django отредактируйте django/db/__init__.py и закомментируйте строку:

signals.request_finished.connect(close_connection)

Этот обработчик сигнала заставляет его отключаться от базы данных после каждого запроса. Я не знаю, какими будут все побочные эффекты от этого, но нет смысла устанавливать новое соединение после каждого запроса; как вы заметили, это снижает производительность.

Я использую это сейчас, но я не выполнил полный набор тестов, чтобы увидеть, что что-то сломалось.

Я не знаю, почему все думают, что для этого нужен новый бэкэнд или специальный пул соединений или другие сложные решения. Это кажется очень простым, хотя я не сомневаюсь, что есть какие-то неясные ошибки, которые заставили их сделать это в первую очередь - с которыми следует иметь дело более разумно; Как вы заметили, 5 мс на каждый запрос довольно много для высокопроизводительного сервиса. (Это занимает у меня 150 мс - я еще не понял, почему.)

Редактировать: еще одно необходимое изменение в django / middleware / Transactions.py; удалите два тестаaction.is_dirty () и всегда вызывайте commit () или rollback (). В противном случае транзакция не будет зафиксирована, если она будет считываться только из базы данных, что оставит открытыми блокировки, которые должны быть закрыты.

15 голосов
/ 15 июля 2011

Я создал небольшой патч Django , который реализует пулы соединений MySQL и PostgreSQL через пулы sqlalchemy.

Это прекрасно работает при производстве http://grandcapital.net/ в течение длительного периода времени.

Патч был написан после небольшого приближения темы.

3 голосов
/ 14 июля 2009

Отказ от ответственности: я не пробовал это.

Полагаю, вам нужно реализовать пользовательскую базу данных. В Интернете есть несколько примеров, показывающих, как реализовать серверную часть базы данных с пулами соединений.

Использование пула соединений, вероятно, будет хорошим решением для вас, поскольку сетевые соединения остаются открытыми, когда соединения возвращаются в пул.

  • Этот пост выполняет это путем исправления Django (один из комментариев указывает на то, что лучше реализовать пользовательский бэкэнд вне основного кода django)
  • Этот пост является реализацией пользовательской базы данных базы данных

В обоих сообщениях используется MySQL - возможно, вы сможете использовать аналогичные методы с Postgresql.

Edit:

  • Книга Django упоминает пул соединений Postgresql, используя pgpool ( tutorial ).
  • Кто-то опубликовал патч для бэкэнда psycopg2, который реализует пул соединений. Я предлагаю создать копию существующей серверной части в вашем собственном проекте и исправить ее.
0 голосов
/ 29 августа 2009

Я сделал небольшой пользовательский бэкэнд psycopg2, который реализует постоянное соединение с использованием глобальной переменной. Благодаря этому я смог увеличить количество запросов в секунду с 350 до 1600 (на очень простой странице с несколькими вариантами выбора) Просто сохраните его в файле с именем base.py в любом каталоге (например, postgresql_psycopg2_persistent) и установите в настройках

DATABASE_ENGINE для имя_проекта.postgresql_psycopg2_persistent

ПРИМЕЧАНИЕ !!! код не является потокобезопасным - вы не можете использовать его с потоками Python из-за непредвиденных результатов, в случае 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

Или вот потокобезопасный, но потоки 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
...