¿Cómo ayuda pgBouncer a acelerar Django?

Tengo algunos comandos de administración que se basan en gevent. Dado que mi comando de administración hace miles de solicitudes, puedo convertir todas las llamadas de socket en llamadas sin bloqueo usando Gevent. Esto realmente acelera mi aplicación ya que puedo hacer solicitudes simultáneamente.

Actualmente, el cuello de botella en mi aplicación parece ser Postgres. Parece que esto se debe a que la biblioteca Psycopg que se usa para conectarse a Django está escrita en C y no admite conexiones asíncronas.

También he leído que usar pgBouncer puede acelerar Postgres por 2X. Esto suena genial, pero sería genial si alguien pudiera explicar cómo funciona y ayuda pgBouncer.

Muchas Gracias

preguntado el 02 de mayo de 12 a las 19:05

También existe la posibilidad de que el modelo de su base de datos no coincida con las consultas que está realizando. Normalmente, la sobrecarga de la red es muy pequeña en comparación con el trabajo necesario para obtener bloques de datos del disco, además: esto no cuesta rendimiento, solo latencia. (excepto tal vez en el caso de conexiones/desconexiones muy frecuentes) -

2 Respuestas

Además de ahorrar la sobrecarga de conexión y desconexión donde esto se hace en cada solicitud, un agrupador de conexiones puede canalizar una gran cantidad de conexiones de clientes a una pequeña cantidad de conexiones de bases de datos reales. En PostgreSQL, el número óptimo de conexiones de bases de datos activas suele estar alrededor ((2 * core_count) + eficaz_spindle_count). Por encima de este número, tanto el rendimiento como la latencia empeoran.

A veces, la gente dirá "Quiero admitir 2000 usuarios, con un tiempo de respuesta rápido". Está bastante garantizado que si intenta hacer eso con 2000 conexiones de base de datos reales, el rendimiento será horrible. Si tiene una máquina con cuatro procesadores de cuatro núcleos y el conjunto de datos activo está completamente en caché, verá un rendimiento mucho mejor para esos 2000 usuarios al canalizar las solicitudes a través de unas 35 conexiones de base de datos.

Para entender por qué eso es cierto, este experimento mental debería ayudar. Considere una máquina de servidor de base de datos hipotética con un solo recurso para compartir: un solo núcleo. Este núcleo dividirá el tiempo por igual entre todas las solicitudes simultáneas sin sobrecarga. Digamos que llegan 100 solicitudes al mismo tiempo, cada una de las cuales necesita un segundo de tiempo de CPU. El núcleo funciona en todos ellos, dividiendo el tiempo entre ellos hasta que todos terminan 100 segundos después. Ahora considere lo que sucede si coloca un grupo de conexiones al frente que aceptará 100 conexiones de clientes pero realiza solo una solicitud a la vez al servidor de la base de datos, colocando en una cola cualquier solicitud que llegue mientras la conexión está ocupada. Ahora, cuando llegan 100 solicitudes al mismo tiempo, un cliente obtiene una respuesta en 1 segundo; otro recibe una respuesta en 2 segundos y el último cliente obtiene una respuesta en 100 segundos. Nadie tuvo que esperar más para obtener una respuesta, el rendimiento es el mismo, pero la latencia promedio es de 50.5 segundos en lugar de 100 segundos.

Un servidor de base de datos real tiene más recursos que se pueden usar en paralelo, pero se aplica el mismo principio, una vez que están saturados, solo daña las cosas al agregar más solicitudes de base de datos simultáneas. En realidad, es peor que el ejemplo, porque con más tareas, tiene más cambios de tareas, mayor contención de bloqueos y caché, contención de línea de caché L2 y L3, y muchos otros problemas que reducen tanto el rendimiento como la latencia. Además de eso, mientras que un alto work_mem la configuración puede ayudar a una consulta de varias maneras, esa configuración es el límite por nodo del plan para cada conexión, por lo que con una gran cantidad de conexiones, debe dejar esto muy pequeño para evitar vaciar el caché o incluso provocar un intercambio, lo que conduce a planes más lentos o cosas como tablas hash que se derraman en el disco.

Algunos productos de bases de datos construyen efectivamente un conjunto de conexiones en el servidor, pero la comunidad de PostgreSQL ha tomado la posición de que dado que el mejor conjunto de conexiones se realiza más cerca del software del cliente, dejarán que los usuarios administren esto. La mayoría de los agrupadores tendrán alguna forma de limitar las conexiones de la base de datos a un número fijo, mientras permiten más solicitudes de clientes concurrentes que eso, poniéndolas en cola según sea necesario. Esto es lo que quiere, y debe hacerse en un transaccional base, no por declaración o conexión.

respondido 20 mar '19, 08:03

Excelente respuesta No podría estar más de acuerdo. - salvaje

Todos estos hippies front-end quieren hacer y romper conexiones lo más rápido posible, y poner a los agrupadores de conexiones al frente si no pueden alcanzar su estado natural alto. Me gusta la fórmula 2*ncore + nspindle. Todo proceso se considera bloqueado en una lectura de disco. - salvaje

@kgrittn Supongo que en su experimento mental anterior, ¿cada consulta tarda un segundo en ejecutarse en ausencia de otras solicitudes? - miguel mior

@MichaelMior: Sí, definitivamente quise poner eso allí, pero lo perdí. Gracias. Editado para incluir esa suposición. - kgrittn

@MichaelMior: en PostgreSQL hay un sistema operativo Procesos por conexión de cliente, y se basa en el sistema operativo para la programación. Los diversos procesos se comunican a través de un segmento de memoria compartida, señales del sistema operativo (si están disponibles) y un socket UDP autorreferenciado. - kgrittn

PgBouncer reduce la latencia en el establecimiento de conexiones sirviendo como un proxy que mantiene un grupo de conexiones. Esto puede ayudar a acelerar su aplicación si está abriendo muchas conexiones de corta duración a Postgres. Si solo tiene una pequeña cantidad de conexiones, no verá una gran ganancia.

contestado el 02 de mayo de 12 a las 19:05

Si he entendido esto correctamente, Django todavía crea conexiones una y otra vez, pero pgBouncer reduce el tiempo necesario para crear esta conexión. Escuché que Django crea una nueva conexión para cada solicitud. Por solicitud, ¿las personas se refieren a una solicitud web para obtener una página (lo que significa que cada comando único ejecutado en el ciclo de una vista pasa por una sola conexión de base de datos) o solicitud significa cada visita de base de datos individual (SELECCIONAR, INSERTAR, ACTUALIZAR y ELIMINAR ) en cuyo caso, cada comando individual se ejecutaría dentro de una nueva conexión aunque estarían en el mismo ciclo de vista: Mridang Agarwalla

Sí, Django creará una nueva conexión, pero la conexión se establecerá más rápido ya que será a una instancia local de PgBouncer. Django usará una nueva conexión para cada solicitud web, no una consulta a la base de datos. - miguel mior

Usted puede encontrar esta pregunta tiene algo más de información interesante. Pero tenga en cuenta que hay una razón por la cual se abren nuevas conexiones en cada solicitud. Si una solicitud encuentra un error, es posible que una transacción no se cierre correctamente (entre otras cosas), lo que genera resultados inesperados. - miguel mior

¿Hay alguna manera de verificar cuántas veces Django crea y destruye una conexión durante el tiempo que se ejecuta mi comando de administración? Podría verificar esto y si DJango está haciendo muchas conexiones nuevas, sería bueno usar pgBouncer; de lo contrario, buscaré otro escenario. Es un comando de administración y no una solicitud de página web, por lo que me pregunto si la conexión de la base de datos realmente se crea solo una vez. Tengo miles de visitas de db desde mi comando de administración. Gracias. - Mridang Agarwalla

Puedes conectarte a la señal. django.db.backends.signals.connection_created y luego hacer algo de registro. (Tenga en cuenta que no querrá hacer esto en producción, ya que agregará una sobrecarga innecesaria). - miguel mior

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.