Estoy ejecutando una aplicación Python Pyramid en un servidor CentOS usando uWSGI y nginx. Estoy usando SQLAlchemy como ORM, MySQLdb como API y MySQL como base de datos. El sitio aún no se ha publicado, por lo que el único tráfico es el mío y otros empleados de la empresa. Compramos algunos datos para llenar la base de datos, por lo que la tabla más grande (y consultada con mayor frecuencia) es ~ 150,000 filas.
Ayer abrí cuatro pestañas nuevas del sitio web en rápida sucesión, y recuperé un par de 502 errores de Bad Gateway. Miré en el registro uWSGI y encontré lo siguiente:
sqlalchemy.exc.OperationalError: (OperationalError) (2006, 'MySQL server has gone away') 'SELECT ge...
Nota importante: este error no se debe al tiempo de espera de MySQL. He estado allí, hecho eso.
Me preguntaba si el problema era causado por solicitudes concurrentes que se servían simultáneamente. Me hice un probador de carga de pobre:
for i in {1..10}; do (curl -o /dev/null http://domain.com &); done;
Efectivamente, dentro de esas diez solicitudes, al menos una arrojaría un error de 2006, muchas veces más. A veces los errores se vuelven aún más extraños, por ejemplo:
sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column 'table.id'"
Cuando la columna definitivamente existe y funciona bien en todas las otras solicitudes idénticas. O este:
sqlalchemy.exc.ResourceClosedError: This result object does not return rows. It has been closed automatically.
Cuando, una vez más, funcionó bien para todas las demás solicitudes.
Para verificar aún más que el problema surgió de las conexiones de bases de datos concurrentes, configuré uWSGI en un solo trabajador y deshabilité los subprocesos múltiples, lo que obligó a que las solicitudes se procesen una por una. Efectivamente, los problemas desaparecieron.
En un intento por encontrar el problema, configuré un registro de errores para MySQL. Con la excepción de algunos avisos durante el inicio de MySQL, permanece vacío.
Aquí está mi configuración de MySQL:
[mysqld]
default-storage-engine = myisam
key_buffer = 1M
query_cache_size = 1M
query_cache_limit = 128k
max_connections=25
thread_cache=1
skip-innodb
query_cache_min_res_unit=0
tmp_table_size = 1M
max_heap_table_size = 1M
table_cache=256
concurrent_insert=2
max_allowed_packet = 1M
sort_buffer_size = 64K
read_buffer_size = 256K
read_rnd_buffer_size = 256K
net_buffer_length = 2K
thread_stack = 64K
innodb_file_per_table=1
log-error=/var/log/mysql/error.log
Google pesado en el error reveló poco, pero sugirió que aumente max_allowed_packet. Lo aumenté a 100M y reinicié MySQL, pero eso no ayudó en absoluto.
Para resumir: las conexiones concurrentes a MySQL causan 2006, 'MySQL server has gone away'
y algunos otros errores extraños. No hay nada relevante en el registro de errores de MySQL.
He estado trabajando en esto durante horas y no he progresado. alguien me puede ayudar?
Respuestas:
También me encontré con esto y encontré la razón y la solución.
La razón por la que esto sucede es que el complemento pyws uwsgi (o más probablemente todos los complementos uwsgi) bifurca () a los nuevos trabajadores después de que la aplicación se carga en el padre. Como resultado, los elementos secundarios heredan todos los recursos (incluidos los descriptores de archivos como la conexión db) del elemento primario.
Puedes leer sobre esto brevemente en la wiki de uwsgi :
Y como ya sabrán, las conexiones y los cursores mysqldb de Python no son seguros a menos que los proteja explícitamente. Por lo tanto, múltiples procesos (como los trabajadores de uwsgi) que usan la misma conexión / cursor mysql simultáneamente lo corromperán.
En mi caso (para la API Gold de King Arthur ) esto funcionó bien cuando creé la conexión MySQL por solicitud en el alcance de otro módulo, pero cuando quería conexiones persistentes para ayudar con el rendimiento, moví la conexión de la base de datos y el cursor al alcance global en El módulo padre. Como resultado, mis conexiones se pisaron entre sí como las suyas.
La solución a esto es agregar la palabra clave "perezosa" (o la opción de línea de comando --lazy) a su configuración uwsgi. Como resultado, la aplicación se bifurcará nuevamente para cada niño en lugar de bifurcarse del padre y compartir la conexión (y pisarla en algún momento, de modo que el servidor MySQL la cierre debido a una solicitud corrupta en algún momento).
Por último, si desea una forma de hacer esto sin modificar su configuración de uwsgi, es probable que pueda usar el decorador @postfork para crear correctamente una nueva conexión de base de datos inmediatamente después de que se bifurca un proceso de trabajo. Puedes leer sobre eso aquí .
Veo por su seguimiento que ya cambió a pgsql, pero aquí está la respuesta para que pueda dormir mejor por la noche y para cualquiera como usted y yo tratando de encontrar la respuesta a esto.
PD: Una vez que entendí el problema (el cursor se corrompió debido a que los trabajadores se pisaron entre sí) pero no me di cuenta del bit sobre fork () y --lazy, estaba considerando implementar mi propio grupo donde los trabajadores " consulte "una conexión mysql desde un grupo en el ámbito global, luego" vuelva a ingresar "justo antes de salir de la aplicación (), sin embargo, es mucho mejor usar --lazy a menos que la carga de su aplicación / web varíe lo suficiente como para estar constantemente creando nuevos trabajadores. Incluso entonces podría preferir --lazy porque es significativamente más limpio que implementar su propio grupo de conexiones db.
editar: aquí hay una descripción más completa de este problema + solución ya que hay una escasez de información sobre él para otros que lo han encontrado: http://tns.u13.net/?p=190
fuente
ping
y otrasmysqladmin
solicitudes. Probablemente fue porque estaba tratando de quitar la base de datos del shell ... pero seguía dando el error "el servidor se ha ido" a ese comando. ¡Gracias de todos modos!