python - djangowhitenoise - heroku postgres
Conexión de base de datos persistente de Django (6)
Creé un pequeño parche de Django que implementa la agrupación de conexiones de MySQL y PostgreSQL a través de la agrupación sqlalchemy.
Esto funciona perfectamente en la producción de http://grandcapital.net/ durante un largo período de tiempo.
El parche fue escrito después de googlear el tema un poco.
Estoy usando django con apache y mod_wsgi y PostgreSQL (todos en el mismo host), y necesito manejar una gran cantidad de solicitudes de página dinámicas simples (cientos por segundo). Me enfrenté al problema de que el cuello de botella es que un django no tiene una conexión de base de datos persistente y se vuelve a conectar en cada solicitud (que lleva cerca de 5 ms). Al hacer un punto de referencia lo conseguí con una conexión persistente que puedo manejar cerca de 500 r / s sin obtener solo 50 r / s.
Alguien tiene algún consejo? ¿Cómo modificar django para usar una conexión persistente? O acelerar la conexión de Python a DB
Gracias por adelantado.
Descargo de responsabilidad: no he intentado esto.
Creo que debes implementar un back-end de base de datos personalizado. Hay algunos ejemplos en la web que muestran cómo implementar un back-end de base de datos con la agrupación de conexiones.
Usar una agrupación de conexiones probablemente sea una buena solución para su caso, ya que las conexiones de red se mantienen abiertas cuando las conexiones se devuelven al grupo.
- Esta publicación logra esto parcheando a Django (uno de los comentarios indica que es mejor implementar un back-end personalizado fuera del código core django)
- Esta publicación es una implementación de un back-end db personalizado
Ambas publicaciones usan MySQL, quizás puedas usar técnicas similares con Postgresql.
Editar:
Django 1.6 ha agregado soporte para conexiones persistentes (enlace a doc para django 1.9) :
Las conexiones persistentes evitan la sobrecarga de restablecer una conexión a la base de datos en cada solicitud. Están controlados por el parámetro CONN_MAX_AGE que define la duración máxima de una conexión. Se puede configurar de forma independiente para cada base de datos.
Hice un pequeño backend customizado de psycopg2 que implementa una conexión persistente usando una variable global. Con esto pude mejorar la cantidad de solicitudes por segundo de 350 a 1600 (en una página muy simple con pocas selecciones) Solo guárdelo en el archivo llamado base.py
en cualquier directorio (por ejemplo, postgresql_psycopg2_persistent) y base.py
en la configuración
DATABASE_ENGINE a projectname.postgresql_psycopg2_persistent
¡¡¡NOTA!!! el código no es inseguro: no se puede usar con subprocesos de Python debido a resultados inexpectables; en el caso de mod_wsgi, utilice el modo de demonio prefork con hilos = 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
O bien, este es un subproceso seguro, pero los subprocesos de python no usan múltiples núcleos, por lo que no obtendrás un aumento de rendimiento como el anterior. Puede usar este también con proceso múltiple.
# 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
En Django trunk, edita django/db/__init__.py
y comenta la línea:
signals.request_finished.connect(close_connection)
Este manejador de señal hace que se desconecte de la base de datos después de cada solicitud. No sé cuáles serán todos los efectos secundarios de hacer esto, pero no tiene sentido iniciar una nueva conexión después de cada solicitud; destruye el rendimiento, como habrás notado.
Estoy usando esto ahora, pero no he hecho un conjunto completo de pruebas para ver si algo se rompe.
No sé por qué todo el mundo piensa que esto necesita un nuevo back-end o un pooler de conexión especial u otras soluciones complejas. Esto parece muy simple, aunque no dudo de que haya algunos obstáculos obscuros que los hicieron hacer esto en primer lugar, lo que debería tratarse con más sensatez; Una sobrecarga de 5ms para cada solicitud es bastante para un servicio de alto rendimiento, como habrás notado. (Me lleva 150ms. No he resuelto por qué todavía).
Editar: otro cambio necesario es en django / middleware / transaction.py; elimine las dos pruebas transaction.is_dirty () y siempre invoque commit () o rollback (). De lo contrario, no comprometerá una transacción si solo lee de la base de datos, lo que dejará bloqueos abiertos que deberían cerrarse.