2012-05-02 30 views
15

Tengo algunos comandos de administración que se basan en gevent. Como mi comando de administración hace miles de solicitudes, puedo convertir todas las llamadas de socket en llamadas no bloqueantes usando Gevent. Esto realmente acelera mi aplicación ya que puedo hacer solicitudes simultáneamente.Cómo ayuda pgBouncer a acelerar Django

Actualmente, el cuello de botella en mi aplicación parece ser Postgres. Parece que esto se debe a que la biblioteca Psycopg que se utiliza 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 en 2X. Esto suena genial, pero sería genial si alguien pudiera explicar cómo funciona pgBouncer y lo ayuda.

Gracias

+0

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

Respuesta

65

Además de guardar la sobrecarga de la conexión & desconectar donde se hace esto en cada solicitud, un grupo de conexión puede canalizar un gran número de conexiones de cliente a un número reducido de conexiones de base de datos reales. En PostgreSQL, la cantidad óptima de conexiones de bases de datos activas suele ser algo cercano ((2 * core_count) + effective_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á prácticamente garantizado que si intentas hacer eso con 2000 conexiones de bases de datos reales, el rendimiento será horrible. Si tiene una máquina con cuatro procesadores de cuatro núcleos y el conjunto de datos activos está completamente en caché, verá un rendimiento mucho mejor para esos 2000 usuarios al canalizar las solicitudes a través de aproximadamente 35 conexiones de bases de datos.

Para entender por qué eso es cierto, este experimento mental debería ayudar. Considere una máquina hipotética de servidor de base de datos con solo un recurso para compartir: un solo núcleo. Este núcleo dividirá el tiempo en partes iguales entre todas las solicitudes concurrentes sin sobrecarga. Digamos que 100 solicitudes vienen todas en el mismo momento, cada una de las cuales necesita un segundo de tiempo de CPU. El núcleo funciona en todos ellos, cortando 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 haga solo una solicitud a la vez al servidor de la base de datos, colocando todas las solicitudes que lleguen mientras la conexión esté ocupada en una cola. Ahora, cuando llegan 100 solicitudes al mismo tiempo, un cliente recibe una respuesta en 1 segundo; otro obtiene una respuesta en 2 segundos, y el último cliente recibe una respuesta en 100 segundos. Nadie tuvo que esperar más tiempo 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 el mismo principio es que, una vez que están saturados, solo hace daño agregando más solicitudes de bases de datos simultáneas. En realidad es peor que el ejemplo, porque con más tareas tiene más conmutadores de tareas, mayor contención de bloqueos y caché, contención de líneas de caché L2 y L3 y muchos otros problemas que reducen el rendimiento y la latencia. Además de eso, mientras que una configuración alta work_mem puede ayudar a una consulta de varias maneras, esa configuración es el límite por nodo de plan para cada conexión, por lo que con un gran número de conexiones debe dejar esto muy pequeño para evitar limpiar el caché o incluso provocar el intercambio, lo que conduce a planes más lentos o cosas tales como que las tablas hash se propaguen al disco. Algunos productos de bases de datos construyen efectivamente un grupo de conexiones en el servidor, pero la comunidad PostgreSQL ha adoptado la posición de que dado que la mejor conexión se realiza más cerca del software del cliente, dejarán que los usuarios lo administren. La mayoría de los usuarios compartidos tendrán alguna manera de limitar las conexiones de la base de datos a un número difícil, al mismo tiempo que permiten más solicitudes concurrentes de clientes que eso, poniéndolos en cola según sea necesario. Esto es lo que desea, y debe hacerse en una base transaccional, no por estado o conexión.

+1

Excelente respuesta. No podría estar más de acuerdo. – wildplasser

+0

Estos hippies front-end todos quieren hacer y romper las conexiones lo más rápido posible, y poner a los poolers de conexión en frente si no pueden alcanzar su estado natural. Me gusta la fórmula 2 * ncore + nspindle. Se considera que todos los procesos están bloqueados en una lectura de disco. – wildplasser

+0

@kgrittn Supongo que en su experimento mental anterior, cada consulta tarda un segundo en ejecutarse en ausencia de otras solicitudes. –

9

PgBouncer reduce la latencia en el establecimiento de conexiones, al servir como un proxy que mantiene una agrupación de conexiones. Esto puede ayudar a acelerar su aplicación si está abriendo muchas conexiones efímeras a Postgres. Si solo tiene un número pequeño de conexiones, no verá mucho de una victoria.

+0

Si lo he entendido correctamente, Django aún 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. A petición, las personas quieren una solicitud web para buscar una página (lo que significa que cada comando ejecutado en el ciclo de una vista pasa por una sola conexión de base de datos) o solicita cada hit de base de datos individual (SELECCIONAR, INSERTAR, ACTUALIZAR y ELIMINAR) en cuyo caso cada comando se ejecutaría dentro de una nueva conexión aunque estuvieran en el mismo ciclo de vista –

+2

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

+1

Puede encontrar que [esta pregunta] (http://stackoverflow.com/questions/1125504/django-persistent-database-connection) tiene información más interesante. Pero tenga en cuenta que hay una razón por la que 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) dando lugar a resultados inesperados. –