Domanda

Sto usando django con Apache e mod_wsgi e PostgreSQL (tutti su stesso host), e ho bisogno di gestire un sacco di semplici richieste di pagine dinamiche (centinaia al secondo). Ho affrontato con il problema che il collo di bottiglia è che un Django non hanno connessione al database persistente e riconnette su ogni richieste (che prende vicino a 5ms). Mentre si fa un punto di riferimento che ho avuto con connessione persistente posso gestire vicino a 500 r / s, mentre senza ottengo solo 50 r / s.

Qualcuno ha qualche consiglio? Come modificare Django per usare connessione persistente? O accelerare il collegamento in pitone a DB

Grazie in anticipo.

È stato utile?

Soluzione

Django 1.6 ha aggiunto connessioni persistenti supporto (link al documento per Django 1.9) :

  

Connessioni permanenti evitare il sovraccarico di ristabilire un   connessione al database in ogni richiesta. Sono controllati dal   parametro CONN_MAX_AGE che definisce la durata massima di un   connessione. Può essere impostato in modo indipendente per ogni database.

Altri suggerimenti

PgBouncer - un Pooler collegamento leggero per PostgreSQL. Caratteristiche:

  • Diversi livelli di brutalità durante la rotazione di connessioni:
    • pool di sessioni
    • Transaction mettere in comune
    • Dichiarazione di mettere in comune
  • requisiti
  • Bassa memoria (2k per connessione di default).

In Django tronco, modificare django/db/__init__.py e commentare la riga:

signals.request_finished.connect(close_connection)

Questa gestore di segnale induce a disconnettersi dal database dopo ogni richiesta. Io non so quello che tutti gli effetti collaterali di fare questo sarà, ma non ha alcun senso per avviare una nuova connessione dopo ogni richiesta; distrugge le prestazioni, come hai notato.

Sto usando questo ora, ma ho havn't fatto una serie completa di test per vedere se qualcosa si rompe.

Non so il motivo per cui tutti pensano questo ha bisogno di un nuovo backend o un Pooler legame speciale o altre soluzioni complesse. Questo sembra molto semplice, anche se non dubito ci sono alcuni trucchi oscuri che li hanno resi fanno in primo luogo - che dovrebbe essere affrontato più sensibilmente; 5ms overhead per ogni richiesta è un bel po 'per un servizio ad alte prestazioni, come hai notato. (Ci metto 150ms -. Il havn't capito perché ancora)

Edit: un altro cambiamento necessario è in Django / middleware / transaction.py; rimuovere le prove due transaction.is_dirty () e sempre chiamare commit () o rollback (). In caso contrario, non sarà commit di una transazione se di sola lettura dal database, che lascerà serrature aperto che dovrebbe essere chiuso.

ho creato un piccolo Django cerotto che implementa il pool di connessioni di MySQL e PostgreSQL tramite SQLAlchemy pooling.

Questo funziona perfettamente su produzione di http://grandcapital.net/ per un lungo periodo di tempo.

La patch è stata scritta dopo googling l'argomento un po '.

. Disclaimer: non ho provato questo

Credo che è necessario implementare un back-end database personalizzato. Ci sono alcuni esempi sul web che mostra come implementare un database di back-end con il pool di connessioni.

Utilizzo di un pool di connessioni probabilmente sarebbe una buona soluzione per voi caso, come le connessioni di rete sono tenuti aperti quando le connessioni vengono restituiti alla piscina.

  • Questo post compie questo patch Django (uno dei commenti sottolinea che è meglio implementare una consuetudine back-end al di fuori del codice di base Django)
  • Questo post è un'implementazione di back-end db una consuetudine

Entrambi i messaggi usano MySQL - forse si è in grado di utilizzare tecniche simili con PostgreSQL

.

Modifica

  • Il Django libro menziona connessione PostgreSQL pooling, utilizzando pgpool ( esercitazione ).
  • una patch per il backend psycopg2 che implementa il pool di connessioni. Suggerisco di creare una copia di back-end esistenti nel proprio progetto e patch che uno.

Ho fatto qualche piccolo backend psycopg2 personalizzato che implementa connessione persistente utilizzando variabile globale. Con questo sono stato in grado di migliorare l'amout di richieste al secondo da 350 a 1600 (su molto semplice pagina con poche seleziona) Basta salvare nel base.py file chiamato in qualsiasi directory (ad esempio postgresql_psycopg2_persistent) e impostare nelle impostazioni

DATABASE_ENGINE a projectname.postgresql_psycopg2_persistent

NOTA !!! il codice non è threadsafe - non è possibile utilizzare con fili di pitone a causa di risultati imprevedibili, in caso di mod_wsgi si prega di utilizzare la modalità daemon prefork con fili = 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

In alternativa ecco un filo di una cassetta di sicurezza, ma le discussioni Python non usano più core, quindi non sarà possibile ottenere tale incremento delle prestazioni come con quello precedente. È possibile utilizzare questo uno con multi processo uno troppo.

# 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top