Pergunta

Estou usando o Django com o Apache e mod_wsgi e PostgreSQL (todos no mesmo host), e eu preciso para lidar com um monte de pedidos de página dinâmica simples (centenas por segundo). Eu enfrentei com problema que o gargalo é que um Django não tem conexão persistente de banco de dados e reconecta em cada solicitações (que leva perto de 5ms). Ao fazer uma referência Eu tenho que, com conexão persistente eu posso lidar com perto de 500 r / s, enquanto sem eu recebo apenas 50 r / s.

Alguém tem algum conselho? Como modificar o Django para usar conexão persistente? Ou acelerar a conexão de python para DB

Agradecemos antecipadamente.

Foi útil?

Solução

Django 1,6 adicionou ligações persistentes suporte (link para o doc para Django 1.9) :

Conexões persistentes evitar a sobrecarga de restabelecer um conexão com o banco de dados em cada solicitação. Eles são controlados pelo CONN_MAX_AGE parâmetro que define o tempo máximo de duração de um conexão. Ele pode ser definido de forma independente para cada banco de dados.

Outras dicas

Tente PgBouncer - um agrupador conexão leve para PostgreSQL. Características:

  • Vários níveis de brutalidade quando rotativas conexões:
    • pooling Session
    • pooling Transaction
    • Declaração pooling
  • requisitos de memória Low (2k por conexão por padrão).

tronco Em Django, editar django/db/__init__.py e comente a linha:

signals.request_finished.connect(close_connection)

Este manipulador de sinal faz com que ele se desconectar do banco de dados depois de cada pedido. Eu não sei o que todos os efeitos colaterais de se fazer isso será, mas isso não faz qualquer sentido para iniciar uma nova conexão depois de cada pedido; ele destrói desempenho, como você observou.

Eu estou usando isso agora, mas eu havn't feito um conjunto completo de testes para ver se alguma coisa quebra.

Eu não sei por que todo mundo acha que isso precisa de uma nova infra-estrutura ou de um agrupador conexão especial ou outras soluções complexas. Isso parece muito simples, embora eu não tenho dúvida, existem alguns truques obscuros que os fizeram fazer isso em primeiro lugar - o que deve ser tratada de forma mais sensata; 5ms sobrecarga para cada pedido é bastante para um serviço de alto desempenho, como você observou. (Leva-me 150ms -. I havn't descobri porque ainda)

Edit: outra mudança necessária está em django / middleware / transaction.py; remover os testes dois transaction.is_dirty () e sempre chamada commit () ou rollback (). Caso contrário, ele não vai cometer uma transação se somente leitura do banco de dados, que vai deixar fechaduras abrir que deve ser fechado.

I criado um pequeno Django remendo que implementa ligação conjugação de MySQL e PostgreSQL via pooling sqlalchemy.

Isso funciona perfeitamente em produção de http://grandcapital.net/ por um longo período de tempo.

O patch foi escrito depois de pesquisar o tema um pouco.

Disclaimer: Eu não tentei este

.

Eu acredito que você precisa para implementar um final personalizado banco de dados de volta. Há alguns exemplos na web que mostra como implementar um back-end banco de dados com o pool de conexão.

Usando um pool de conexão provavelmente seria uma boa solução para você caso, como as conexões de rede são mantidas abertas quando as conexões são devolvidos para a piscina.

  • Este post Realiza este remendando Django (um dos comentários assinala que é melhor para implementar um fora end personalizado volta do núcleo django código)
  • Este post é uma implementação de um final personalizado db volta

As duas mensagens usar MySQL -. Talvez você é capaz de usar técnicas semelhantes com o PostgreSQL

Editar:

  • O Livro Django menciona Postgresql pool de conexão, utilizando pgpool ( tutorial ).
  • Alguém postou um patch para o backend psycopg2 que o pool de conexão implementos. Sugiro a criação de uma cópia de back-end existente em seu próprio projeto e remendar aquele.

Eu fiz algumas pequenas costume backend psycopg2 que implementa conexão persistente usando variável global. Com isso eu era capaz de melhorar a quantia de pedidos por segundo de 350 a 1600 (na página muito simples com poucas seleciona) Apenas salve-o no arquivo chamado base.py em qualquer diretório (por exemplo postgresql_psycopg2_persistent) e conjunto de configurações

DATABASE_ENGINE para projectname.postgresql_psycopg2_persistent

NOTA !!! o código não é threadsafe - você não pode usá-lo com tópicos python por causa de resultados unexpectable, em caso de mod_wsgi por favor use o modo daemon prefork com fios = 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

Ou aqui é um segmento de um seguro, mas tópicos python não usar múltiplos núcleos, assim você não terá tal aumento de desempenho como com o anterior. Você pode usar este com vários processo de um também.

# 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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top