MySQL: no se puede perdonar la ejecución





El sitio web y la tienda online "Eldorado" tiene alrededor de 40 mil compras diarias. Probablemente no sea necesario explicar qué significa esto para el negocio de la empresa.



Históricamente, la tienda funciona con el motor Bitrix con una gran cantidad de código personalizado y complementos. El almacenamiento es un clúster MySQL con cuatro servidores maestros.



Un número importante de empresas tienen aplicaciones monolíticas y muchas tienen que trabajar con ellas. Hay muchas formas de lidiar con el monolito, pero, desafortunadamente, pocas personas escriben sobre las exitosas. Espero que te interese la historia sobre cómo apuntalamos nuestro monolito (hasta que lo vimos).



Somos conscientes de que la arquitectura masiva puede traer muchos problemas. Pero es fácil destruirlo, con una simple decisión obstinada, es imposible: las ventas van, el sitio debe funcionar, los cambios para los usuarios no deben ser radicales. Por lo tanto, la transición de un monolito a un conjunto de microservicios lleva tiempo, que debemos mantener: para garantizar que el sistema esté operativo y su resistencia al estrés.



Cual fue el problema



Durante mucho tiempo, el clúster de la base de datos en el sitio web eldorado.ru se construyó de acuerdo con el siguiente esquema:





Todos los maestros en este esquema trabajan simultáneamente y todos están en modo activo, reproduciendo secuencialmente el flujo de replicación ... M1-> M2- > M3-> M4-> M1 -> M2-> M3-> M4-> M1-> M2 ...



En nuestra configuración, esta configuración dio la única ventaja: permitió que el sistema funcionara y mantuviera su carga. El hecho es que el balanceador de solicitudes de la aplicación, después de cualquier actualización para garantizar la coherencia, cambia todo el flujo de lectura a este maestro, y un maestro una vez no fue suficiente para contener todo el flujo de lectura.



Pero tal esquema no podría proporcionar confiabilidad ni velocidad de trabajo. Aunque parecía simple, tenía varios defectos. Ella tardó mucho en actualizar los datos en el clúster: en el peor de los casos, había hasta cinco brazos de replicación (según en qué maestro se iniciaron inicialmente los cambios). Debido a estos retrasos, surgieron muchos problemas tanto en el funcionamiento del sitio como a la hora de realizar pedidos en la tienda online.



Contras de este esquema :



  • Los esclavos de la zona más alejada del maestro activo reciben actualizaciones de datos en el peor de los casos solo después de 4 veces el tiempo de ejecución de la transacción, a veces hubo frenéticos retrasos en la replicación;
  • Cualquier falla en cualquiera de los maestros conduce a una inconsistencia de datos en todo el clúster hasta que se elimina;
  • (- — );
  • ;
  • , ( , , );
  • UPDATE/DELETE SELECT ;
  • , slave_status seconds_behind_master.


Puede emular este comportamiento en sus entornos de prueba habilitando un retraso de replicación artificial de 1-2 segundos en los esclavos (lo cual hicimos), esta es una excelente manera de probar su aplicación para ver si está lista para tales arquitecturas distribuidas a través de la opción MASTER_DELAY = N .



y, por último, la transición a otra base de datos en nuestro caso no es una opción, ya que el sistema es demasiado grande escala y mucho en ella está ligada a la utilización de características de MySQL, e incluso a los matices de su optimizador de consultas internas.



Como lo solucionamos



No queríamos cambiar el esquema nosotros mismos (esta es una operación arriesgada) y comenzamos por buscar una empresa de consultoría que pudiera proponer e implementar una nueva arquitectura, y hacerlo para que el sitio permanezca accesible y el cambio no se note. Entre estas empresas se encontraban los integradores y desarrolladores de software más importantes.



Algunas empresas simplemente no nos respondieron (y esto es normal), mientras que otras escribieron que no estaban preparadas para asumir esa tarea. Al mismo tiempo, la cuestión del posible costo del proyecto ni siquiera surgió.

Si los grandes integradores no quieren involucrarse en una tarea, entonces se ha vuelto doblemente interesante resolverla ellos mismos. Teníamos un buen equipo a nuestra disposición, los problemas de estabilidad y tolerancia a fallas estaban en la segunda prioridad, y al principio queríamos acelerar de alguna manera la transferencia de datos. Varias opciones que aparecieron en las versiones 5.6 y 5.7 de MySQL funcionaron bien para esto.



Es cierto que la documentación insinuaba de forma transparente que no sería posible habilitarlos simplemente, tk. en el ring definitivamente habrá un esclavo con una versión más pequeña, pero aquí es así:

El maestro 5.7 puede leer los registros binarios antiguos escritos antes de la actualización y enviarlos a los esclavos 5.7. Los esclavos reconocen el formato antiguo y lo manejan correctamente.



Los registros binarios creados por el maestro después de la actualización están en formato 5.7. Estos también son reconocidos por los 5.7 esclavos.



En otras palabras, al actualizar a MySQL 5.7, los esclavos deben ser MySQL 5.7 antes de poder actualizar el maestro a 5.7.


Para las pruebas, fue suficiente para nosotros levantar el anillo de prueba, por ejemplo, a través de mysqld_multi, y ejecutar consultas típicas en él (puede incluso en el mismo host, 4 instancias en diferentes puertos con diferentes conjuntos de compensación), algo como esto:



mysql -h127.0.0.1 -P 3302 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
mysql -h127.0.0.1 -P 3302 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3301, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master1-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
mysql -h127.0.0.1 -P 3303 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
mysql -h127.0.0.1 -P 3303 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3302, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master2-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
mysql -h127.0.0.1 -P 3304 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
mysql -h127.0.0.1 -P 3304 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3303, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master3-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
mysql -h127.0.0.1 -P 3301 -e "RESET SLAVE; RESET MASTER; SHOW MASTER STATUS\G;"
mysql -h127.0.0.1 -P 3301 -e "CHANGE MASTER TO MASTER_HOST='127.0.0.1', MASTER_PORT=3304, MASTER_USER='root', MASTER_PASSWORD='', MASTER_LOG_FILE='master4-binlog.000001', MASTER_LOG_POS = 107;START SLAVE;"
      
      





Después de eso, puede cambiar la versión de cualquier instancia ejecutando la configuración con el puerto deseado con otro binario, que se colocó junto a él, y haciendo mysql_upgrade para las tablas de datos / sistema.



En oferta, podríamos, por un tiempo limitado, enviar todo el tráfico a un solo maestro, actualizar otro en este momento, luego cambiarlo y volver a actualizar todos los demás. Pero para ello, era necesario asegurar la compatibilidad del formato binlog entre versiones, para que absolutamente todas las transacciones se perdieran con éxito.



Un poco más de documentación útil para nuestro caso:

Para evitar incompatibilidades, configure las siguientes variables en el maestro MySQL 5.6:



binlog_checksum=NONE
binlog_row_image=FULL
binlog_rows_query_log_events=OFF
log_bin_use_v1_row_events=1 (NDB Cluster only) 
      
      





Esta bandera resultó ser obligatoria para nosotros, aunque no usamos ningún NDB, sin él finalizó la replicación entre los servidores 5.6 - 5.5, y mysqlbinlog lee el log sin esta opción con el error ERROR: Error en Log_event :: read_log_event () : 'Error de comprobación de cordura', data_len: 8203, event_type: 30, si está habilitado, entonces todo comienza y funciona.

No incluimos el GTID, porque Además del requisito de compatibilidad con todas las herramientas antiguas, objetivamente no vemos suficientes ventajas para la transición.



gtid_mode=OFF
      
      





La prueba más sencilla para comprobar la exactitud de la replicación es subir un volcado al servidor con 5.5 y al servidor con 5.6 y ver si todo va bien.



Desafortunadamente, aunque era de esperar, las pruebas no tuvieron éxito.



Last_Error: Column 18 of table 'eldorado2.b_adv_banner' cannot be converted from type '<unknown type>' to type 'datetime'
      
      





datetime en 5.6 es especial, se le agregan microsegundos, por lo que en 5.6 tiene una nueva fecha y hora, desconocida en 5.5



versión 5.6 - puede funcionar en un clúster en un anillo en paralelo con 5.5, si al mismo tiempo no se crean campos en cualquiera de las tablas que se ejecutan a través de la replicación con nuevos tipos de campos. (datetime 5.6! = ​​datetime 5.5, similar a time, timestamp, hay más de 240 campos de este tipo en nuestra base de datos).



No podíamos garantizar completamente la ausencia de DDL con estos campos y no queríamos poner en peligro el rendimiento de todo el clúster. Pero teníamos un Plan B más seguro.



Significó la presencia de hardware adicional para maniobras y levantar una copia completa del clúster cercano, afortunadamente, teníamos dicho hardware. Y dado que existe tal posibilidad, entonces era necesario crear un clúster "normal" a la vez.



Pero, al mismo tiempo, es necesario asegurar la preservación de la operatividad de todas las herramientas actuales de monitoreo, depuración y análisis binlog y eliminar en la medida de lo posible todas las deficiencias existentes de la arquitectura actual.



Replicación multicanal



Se necesita una solución milagrosa para mantener intactos los sitios y alimentar a los administradores. Esta es la replicación multicanal. A priori no confiábamos en nuevas oportunidades y no estábamos seguros de la tecnología, no pudimos encontrar esa información o casos en ningún lado, hay poca experiencia pública en la gran producción.



Por eso, pensamos en todo nosotros mismos, el plan era el siguiente:



  • : 5.7, ;
  • ;
  • , — , , .








— , , , . , , ? . , « »! , - !

( « »)


En el esquema de destino, 4 maestros son 4 flujos de grabación independientes para cada esclavo, que se procesan de forma independiente.



En todos los maestros, ahora era posible desactivar log_slave_updates: simplemente no necesitan retransmitir nada en ningún lugar, envían todos los cambios en la transmisión principal (=> la carga en el maestro es aún menor).



Y al mismo tiempo, también puede habilitar el formato binlog mínimo y el procesamiento paralelo de transacciones en el camino (de manera bastante condicional, debe comprenderlo correctamente):



slave_parallel_workers=5
slave_parallel_type=LOGICAL_CLOCK
binlog_row_image=minimal
      
      





Con esta configuración, podríamos cambiar la carga a un nuevo clúster en cualquier momento, pero esta ruta es unidireccional y no permite la reversión.



Durante la vida útil de las conexiones al clúster antiguo, la opción log_slave_updates en un punto de entrada maestro al nuevo clúster todavía está allí y, por lo tanto, todos los cambios de las conexiones al clúster "antiguo" se entregan perfectamente al nuevo, e inmediatamente después mueren, esta opción estaba deshabilitada, la aplicación hasta este momento miraba a otros 3 maestros y los flujos de datos no se cruzaban de ninguna manera.



Como resultado, obtuvimos el siguiente conjunto de ventajas:



  • Si una solicitud larga está bloqueando algo, esto afecta solo a uno de los 4 subprocesos de replicación y no afecta a los demás de ninguna manera;
  • El nuevo formato binlog, que antes era imposible solo por la versión MySQL, ahora ocupa varias veces menos espacio y, en consecuencia, tráfico, debido a esto, pueden pasar muchos más cambios por todo el clúster;
  • Ahora puede apagar cualquier maestro absolutamente sin dolor sin afectar todo lo demás;
  • Los accidentes de los maestros ahora no dan tanto miedo, ahora puede clonar cualquier servidor en un minuto en cualquier situación incomprensible, volver a generar el server_id, crear créditos para el acceso de esclavos y el nuevo maestro está listo.


También hay un "menos":



  • Cada maestro tiene muchos más esclavos y es más fácil toparse con el canal (de hecho, esto no es un aumento en el tráfico, sino una redistribución en el tiempo y el espacio).


¿Qué dio el nuevo esquema?



El cambio al nuevo esquema resultó exitoso, lo llevamos a cabo en un día, el 28 de agosto de 2020. La experiencia de utilizar la nueva arquitectura ha demostrado que el número de problemas de replicación se ha reducido de tres a cuatro veces (es imposible eliminarlos por completo). La estabilidad del sistema ha aumentado. Y el resultado principal fue un aumento en el rendimiento máximo del sistema. Si los desarrolladores anteriores podían culpar a la replicación de cualquier problema incomprensible, ahora no les funciona.



La cantidad de problemas de clientes causados ​​por demoras en la replicación se ha reducido varias veces, lo que significa que los clientes están experimentando al menos un poco menos de dolor. Ahora podemos apagar cualquier servidor maestro en cualquier momento para poder trabajar en él. Esto no afecta a todo el clúster ni detiene el proceso de replicación.



El clúster sirve al sitio principal "Eldorado": en su mayor parte, una antigua aplicación monolítica con tarjetas de productos, una cuenta personal, una canasta, procesamiento de pedidos, un centro de llamadas, etc. En el momento de escribir este artículo, la carga de lectura total en el clúster (solo en los esclavos) es de 40k rps, aproximadamente 5k rps por servidor de base de datos, excluyendo la carga técnica en esclavos técnicos individuales, que es significativamente mayor en las horas pico. Puede que no parezca mucho, pero hay que tener en cuenta la naturaleza y complejidad de estas consultas.



Realmente esperamos que nuestra experiencia sea de utilidad para alguien. Además de la replicación multicanal, también usamos muchas cosas interesantes, como blackhole y tablas federadas, también te permiten quitar muchos dolores de cabeza (y agregar un poco para los que no entienden por qué se necesitan), si alguien es interesado en los matices y cualquier otra pregunta sobre nuestro MySQL - Bienvenidos en los comentarios.



Durante medio año de operación comercial, todavía no hemos encontrado ningún problema relacionado con el multicanal, y definitivamente podemos recomendar una configuración como suficientemente tolerante a fallas y confiable.



El monolito en sí está ahora en el proceso de dividirse en una serie de servicios separados e independientes, algunos de los cuales compartiremos y le contaremos sobre nuestra experiencia, estad atentos.



Un agradecimiento especial a mi excelente equipo, ¡no lo hubiéramos hecho sin ella!



PD Por cierto, todavía necesitamos programadores talentosos . Si eres así, ven , será interesante.



All Articles