¿Cómo escalar php5 + MySQL por encima de 200 solicitudes / segundo?

16

Estoy ajustando mi página de inicio para el rendimiento, actualmente maneja alrededor de 200 solicitudes / segundo en 3.14.by que come 6 consultas SQL, y 20 req / segundo en 3.14.by/forum que es el foro phpBB.

Por extraño que parezca, los números son casi iguales en algunos VPS y servidores Atom 330 dedicados.

El software del servidor es el siguiente: Apache2 + mod_php prefork 4 childs (probamos diferentes números aquí), php5, APC, nginx, memcached para el almacenamiento de sesiones PHP.

MySQL está configurado para consumir aproximadamente el 30% de la RAM disponible (~ 150Mb en VPS, 700Mb en un servidor dedicado)

Parece que hay un cuello de botella en algún lugar que no me permite ir más alto, ¿alguna sugerencia? (es decir, sé que hacer menos de 6 SQL lo haría más rápido, pero esto no parece ser un factor limitante, ya que sqld no come más que unos pocos% en la parte superior debido a consultas en caché)

¿Alguien ha probado que patear apache2 preformado y dejar solo nginx + php es mucho más rápido?

Algunos puntos de referencia más

Small 40-byte static file: 1484 r/s via nginx+apache2, 2452 if we talk to apache2 directly. 
Small "Hello world" php script: 458 r/s via ngin+apache2.

Actualización: Parece que el cuello de botella es el rendimiento de MySQL en los datos en caché. La página con SQL único muestra 354req / sec, con 6 SQL's - 180 req / sec ¿Qué crees que puedo modificar aquí? (Puedo desembolsar 100-200Mb para MySQL)

[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket      = /var/run/mysqld/mysqld.sock
nice        = 0

[mysqld]
default-character-set=cp1251
collation-server=cp1251_general_cs

skip-character-set-client-handshake

user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
skip-external-locking

bind-address        = 127.0.0.1

key_buffer      = 16M
max_allowed_packet  = 8M
thread_stack        = 64K
thread_cache_size   = 16
sort_buffer_size    = 8M
read_buffer_size    = 1M

myisam-recover      = BACKUP
max_connections        = 650
table_cache            = 256
thread_concurrency     = 10

query_cache_limit       = 1M
query_cache_size        = 16M

expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 8M

[mysql]
[isamchk]
key_buffer      = 8M

!includedir /etc/mysql/conf.d/
BarsMonster
fuente
¿Por qué estás usando Apache y nginx?
jamieb
Esa es una configuración común, Apache2 a PHP y varias aplicaciones que requieren infraestructura apache, nginx para reducir la huella de memoria apache2 en la carga.
BarsMonster
En realidad, no entiendo tu problema. ¿Su sitio es actualmente lento? Si es así, ¿qué tan lento es? ¿Y cuánto deseas acelerarlo? ¿Has intentado perfilar alguna parte de tu sitio para determinar dónde está el cuello de botella?
jamieb
Está en la descripción: ahora está en 180-200 solicitudes / segundo. Si bien esto es mucho más que suficiente para una página de inicio, quiero modificar esta configuración para que otros sitios creados en la misma base de código funcionen más rápido. Idealmente quiero saturar la conexión de 100Mbit con páginas dinámicas :-)
BarsMonster
2
"Solicitudes por segundo" no es realmente una métrica significativa en este contexto. Mi netbook podría manejar "200 solicitudes por segundo". Debe decirnos qué tiempo de respuesta desea alcanzar con ese tipo de tasa de conexión.
jamieb

Respuestas:

29

Obviamente, hay muchas cosas que puedes probar. Su mejor opción es perseguir sus registros en busca de consultas que no usen índices (habilitar registros para esos) y otras consultas no optimizadas. He compilado una gran lista de opciones relacionadas con el rendimiento a lo largo de los años, por lo que he incluido un pequeño subconjunto aquí para su información, espero que ayude. Aquí hay algunas notas generales sobre cosas que puede probar (si aún no lo ha hecho):

MySQL

  • query_cache_type = 1: las consultas SQL de caché están activadas. Si se establece en 2, las consultas solo se almacenan en caché si se les pasa la sugerencia SQL_CACHE. De manera similar con el tipo 1, puede deshabilitar la memoria caché para una consulta particular con la sugerencia SQL_NO_CACHE
  • key_buffer_size = 128M (predeterminado: 8M) - memoria intermedia para índices de tabla MyISAM. En servidores dedicados, intente establecer key_buffer_size en al menos una cuarta parte, pero no más de la mitad, de la cantidad total de memoria en el servidor
  • query_cache_size = 64M (predeterminado: 0) - tamaño del caché de consultas
  • back_log = 100 (predeterminado: 50, máx .: 65535): la cola de solicitudes de conexión pendientes. Solo importa cuando hay muchas conexiones en poco tiempo
  • join_buffer_size = 1M (predeterminado: 131072): un búfer que se usa cuando se realizan escaneos completos de la tabla (sin índices)
  • table_cache = 2048 (predeterminado: 256): debe ser max_user_connections multiplicado por el número máximo de JOIN que contiene su consulta SQL más pesada. Use la variable "open_tables" en horas pico como guía. Observe también la variable "Open_tables": debe estar cerca de "Open_tables"
  • query_prealloc_size = 32 K (por defecto: 8K) - memoria persistente para las declaraciones de análisis y ejecución. Aumentar si tiene consultas complejas
  • sort_buffer_size = 16M (por defecto: 2M) - ayuda con la clasificación (operaciones ORDER BY y GROUP BY)
  • read_buffer_size = 2M (por defecto: 128K) - Ayuda con exploraciones secuenciales. Aumentar si hay muchas exploraciones secuenciales.
  • read_rnd_buffer_size = 4M - ayuda a acelerar tabla MyISAM hasta después de leer especie
  • max_length_for_sort_data - tamaño de fila para almacenar en lugar del puntero de fila en el archivo de clase. Puede evitar mesa de lecturas aleatorias
  • key_cache_age_threshold = 3000 (por defecto: 300) - tiempo para mantener caché clave en la zona caliente (antes de ser degradado a calentar)
  • key_cache_division_limit = 50 (por defecto: 100) - permite un mecanismo de caché desalojo más sofisticado (dos niveles). Denota el porcentaje de mantener el nivel inferior. DELAY_KEY_WRITE = ALL - key buffer no se vacía de la tabla en cada actualización del índice, pero sólo cuando se cierra la tabla. Esto acelera las escrituras de claves mucho, pero si se utiliza esta función, se debe añadir la comprobación automática de todas las tablas MyISAM arrancando el servidor con la recuperación de --myisam-BACKUP, opción FORCE =
  • memlock = 1 - proceso de bloqueo en la memoria (para reducir el intercambio de entrada / salida)

apache

  • cambiar el método de desove (a mpm por ejemplo)
  • logs desactivar si es posible
  • AllowOverride None: siempre que sea posible, deshabilite .htaccess. Se detiene Apache para buscar archivos .htaccess si no se utilizan por lo que ahorra una petición de archivo de búsqueda
  • SendBufferSize: establecer en el sistema operativo predeterminado En redes congestionadas, debe establecer este parámetro cerca del tamaño del archivo más grande que normalmente se descarga
  • KeepAlive Off (predeterminado): e instala Lingerd para cerrar correctamente las conexiones de red y es más rápido
  • DirectoryIndex index.php: mantenga la lista de archivos lo más breve y absoluta posible.
  • Opciones FollowSymLinks - para simplificar el proceso de acceso a archivos en Apache
  • Evite usar mod_rewrite o al menos expresiones regulares complejas
  • ServerToken = prod

PHP

  • variables_order = "GPCS" (si no necesita variables de entorno)
  • register_globals = Off - además de ser un riesgo de seguridad, también tiene un impacto en el rendimiento
  • Mantenga include_path lo más mínimo posible (evita búsquedas adicionales del sistema de archivos)
  • display_errors = Off - Deshabilita mostrar errores. Muy recomendable para todos los servidores de producción (no muestra mensajes de error feos en caso de un problema).
  • magic_quotes_gpc = Desactivado
  • magic_quotes _ * = Off
  • output_buffering = On
  • Deshabilite el registro si es posible
  • expose_php = Desactivado
  • register_argc_argv = Desactivado
  • always_populate_raw_post_data = Desactivado
  • coloque el archivo php.ini donde php lo buscaría primero.
  • session.gc_divisor = 1000 o 10000
  • session.save_path = "N; / path" - Para sitios grandes considere usarlo. Divide los archivos de sesión en subdirectorios

Ajustes del sistema operativo

  • Monte los discos duros usados ​​con la opción -o noatime (sin tiempo de acceso). Agregue también esta opción al archivo / etc / fstab.
  • Ajuste el / proc / sys / vm / swappiness (de 0 a 100) para ver cuál tiene los mejores resultados
  • Usar discos RAM - mount --bind -ttmpfs / tmp / tmp
Ivan Peevski
fuente
Esa es una buena lista, ya he tenido la mayoría de estos, y cuando agregué cosas restantes, el rendimiento no ha aumentado. Parece que el cuello de botella está en algún lugar entre PHP y MySQL que no puede manejar más de 800 solicitudes por segundo desde el caché de consultas ...
BarsMonster
Ok, ¿cómo te conectas a la base de datos (mysql_pconnect () en lugar de mysql_connect ())? ¿Utiliza conexiones persistentes? prueba en ambos sentidos ...
Ivan Peevski
Ya estoy en pconnect y la agrupación de conexiones está habilitada en php.ini ...: -S
BarsMonster
Solo por el bien de la integridad, trataría de conectarme. He visto casos (especialmente en pruebas de carga) en los que eso funciona mejor.
Ivan Peevski
1

Si el cuello de botella no es CPU, entonces es IO, ya sea red o disco. Entonces ... necesitas ver cuánto está pasando IO. No hubiera pensado que es la red (a menos que esté en un enlace half-duplex de 10 Mbps, pero vale la pena verificar el interruptor en caso de que la detección automática no esté haciendo su trabajo correctamente).

Eso deja el disco IO, que puede ser un factor importante, especialmente en los VPS. Use sar o iostat para echar un vistazo a los discos, luego busque en Google cómo encontrar más detalles si su disco se está usando mucho.

gbjbaanb
fuente
Sí, la red no es el problema: cuando se ejecuta ab desde el servidor local, el rendimiento es el mismo. Verifiqué el tiempo de espera (está por debajo del 0,01%), básicamente todo está en la memoria caché del disco y no hay escrituras de disco involucradas en la solicitud de procesamiento (todos los registros están deshabilitados).
BarsMonster
1

Buscaría el almacenamiento en caché con Nginx ( memcached ) o Varnish .

Por lo menos, debe servir archivos estáticos con Nginx como SaveTheRbtz, dijo.

Espennilsen
fuente
Estas son páginas dinámicas, por lo que preferiría no almacenarlas en caché.
BarsMonster
1
memcached no es una aplicación de almacenamiento en caché tradicional y puede hacer maravillas para páginas dinámicas. Se encuentra entre el DB y su aplicación. Su aplicación primero consulta memcached para un objeto, si no está allí, luego se carga desde la base de datos. El efecto neto es que está utilizando RAM para atender sus solicitudes de base de datos en lugar del almacenamiento persistente mucho más lento en la base de datos.
jamieb
Memcache se puede usar con nginx, esa es una característica conocida. No se utiliza un almacenamiento persistente más lento, todo está en la caché de consultas en MySQL.
BarsMonster
Memcached y el caché de consultas de MySQL no son realmente comparables; Ni siquiera hacen lo mismo. Eres bastante rápido en derribar casi todas las sugerencias publicadas aquí sin molestarte en entenderlas. Te recomiendo que seas un poco más de mente abierta.
jamieb
Entiendo claramente la diferencia entre caché de consultas memcached y MySQL. Pero debido al hecho de que todo está en la caché de consultas con una proporción de aciertos del 100%, no lo llamaría "almacenamiento persistente lento". La respuesta original de ayer fue sobre el uso de NginX + Memcached, que es un escenario bastante común para almacenar en caché páginas enteras. El almacenamiento en caché de objetos individuales es otro escenario totalmente diferente. Si bien usar memcached en frente de MySQL está sobre la mesa, estoy pensando en obtener más jugo sin él por ahora (ya que requeriría bastantes cambios de código).
BarsMonster
1

Como el servidor no parece ser un problema, quizás lo sea el generador de carga. Intenta ejecutarlo en varias máquinas.

OliverS
fuente
El rendimiento es el mismo incluso si lo ejecuto desde el servidor. No importa cómo manu conexiones concurrentes - 10 o 50. La prueba de carga se realiza a través de ab -c 10 -t 10
BarsMonster
1

Me parece que podría estar alcanzando la cantidad máxima de conexiones que permite Apache. Eche un vistazo a su configuración de Apache. El aumento del límite del servidor y los clientes máximos debería ayudar si aún no está sujeto a algún otro límite, como E / S o memoria. Mire los valores presentes para mpm_prefork_module o mpm_worker_module y ajústelos de acuerdo a sus necesidades.

ServerLimit 512
MaxClients 512
Erik Giberti
fuente
Bueno, ¿realmente necesito esto siempre que tenga nginx frente a apache2, así que creo que no hay mucho sentido de tener más que núcleos físicos * 2 procesos Apache2 ...
BarsMonster
Acabo de verificar esto. El aumento del número de procesos de Apache2 de 4 a 16 no mejoró el rendimiento (incluso se redujo en un 0,5%). El aumento del número de trabajadores de nginx a 2 o 4 no mejoró nada.
BarsMonster
1
Si sus datos son bastante estáticos, es decir, no actualizan cada carga de página, puede aumentar su query_cache. MySQL mantendrá el conjunto de resultados de esa manera y extraerá de la memoria. Sin embargo, si la tabla que se está almacenando en caché recibe escrituras durante ese tiempo, invalida la memoria caché (incluso si los datos no se ven afectados), lo que hace que se desperdicie memoria.
Erik Giberti
Ahora mismo veo 100% velocidad de la caché de consultas éxito, y MySQL es todavía se siente lento ...
BarsMonster
1
Agregue skip-name-resolve a su archivo de configuración MySQL. Eso guardará una búsqueda de DNS en cada conexión al servidor. El inconveniente aquí es que todas las conexiones deberán estar bloqueadas por IP (suponiendo que no esté usando '%'). Si el SQL está en el mismo servidor y no es necesario acceder a él en ningún lugar que no sea localhost, también puede agregar redes de omisión para eliminar toda la pila TCP / IP. Sin embargo, creo que el cuello de botella es Apache.
Erik Giberti
0

¿Es esta carga generada por una herramienta o cargas del mundo real?

Es posible que desee verificar memcached. He visto problemas con altas tasas de conexión que causan latencia en la aplicación.

Si usa un generador de carga, ¿qué obtiene al golpear una pequeña página estática?

Durante las cargas, es posible que desee comprobar la pila de red para las condiciones TIME_WAIT. Quizás esté completando su cola de conexión.

Hay alrededor de 100 razones más y elementos que puede ver, pero sin más información, solo estoy haciendo conjeturas en este momento.

jeffatrackaid
fuente
Se prueba a través de la URL ab-c 10 -t 10 que estoy comparando desde el propio servidor, por lo que la red no debería ser el problema. He publicado más puntos de referencia por su solicitud.
BarsMonster
No gastaría demasiado esfuerzo sintonizando con ab. Es posible que no se traduzca bien en el rendimiento del mundo real. Lo que puede hacer es diseccionar su aplicación y probar cada componente. Por ejemplo, acceda al servidor apache directamente con solo una página estática muy pequeña. Esto le dará una idea de su requerimiento máximo por segundo en el backend. Ponga nginx al frente, vuelva a probar llamando al mismo archivo de fondo. Luego pruebe con una simple página de php tipo "hola mundo". A veces todas las capas pueden enmascarar algo simple. Además, observe las conexiones durante la prueba. Asegúrese de que su pila de red no se esté llenando.
jeffatrackaid
Hice estos puntos de referencia ayer, y están en la descripción original actualizada de la pregunta. Además, las pruebas se realizan en localhost, por lo que la red no es un problema.
BarsMonster
La red puede ser un problema incluso cuando se realiza en un host local. No es probable en su caso, pero puede causar problemas. Por lo menos ahora tiene un límite superior de ~ 450 req / seg con su configuración PHP actual. El siguiente paso es colocar una llamada a la base de datos y ver cómo cambia eso. Me gusta separar esto cuando hago ajustes de alto nivel, ya que realmente puede ayudarte a identificar la capa que causa la mayoría de los problemas.
jeffatrackaid
-1

El 99% de las veces, problemas como este se remontan a la base de datos. Asegúrate de que tus índices de bateo sean los primeros. Si eso no funciona, comienza a almacenar en caché todo lo que puedas.


fuente
Es todos los índices y como estaba diciendo, incluso está afectando a MySQL caché de consulta en el 100% de los casos
BarsMonster
-1

Le recomiendo que use (si es posible) un agrupador de conexiones para mantener la base de datos conectada a sus aplicaciones web (no es necesario volver a conectarse en cada solicitud). Eso puede hacer una gran diferencia de velocidad.

Además, intente analizar todas sus consultas con EXPLAIN (y ¿por qué no perfilar sus consultas con SHOW PROFILE?).

Kedare
fuente
Todas las consultas usan índices. Se utiliza el grupo de conexión MySQL.
BarsMonster