El equipo de LMAX hizo una presentación sobre cómo pudieron hacer 100k TPS a menos de 1 ms de latencia . Han respaldado esa presentación con un blog , un documento técnico (PDF) y el código fuente en sí.
Recientemente, Martin Fowler publicó un excelente artículo sobre la arquitectura LMAX y menciona que ahora pueden manejar seis millones de pedidos por segundo y destaca algunos de los pasos que el equipo tomó para subir otro orden de magnitud en el rendimiento.
Hasta ahora he explicado que la clave de la velocidad del procesador de lógica de negocios es hacer todo de forma secuencial, en memoria. Simplemente hacer esto (y nada realmente estúpido) permite a los desarrolladores escribir código que pueda procesar 10K TPS.
Luego descubrieron que concentrarse en los elementos simples de un buen código podría llevar esto al rango de 100K TPS. Esto solo necesita código bien factorizado y métodos pequeños, esencialmente esto permite que Hotspot haga un mejor trabajo de optimización y que las CPU sean más eficientes en el almacenamiento en caché del código mientras se ejecuta.
Se necesitó un poco más de inteligencia para subir otro orden de magnitud. Hay varias cosas que el equipo de LMAX encontró útiles para llegar allí. Una fue escribir implementaciones personalizadas de las colecciones de Java que fueron diseñadas para ser amigables con la caché y cuidadosas con la basura.
Otra técnica para alcanzar ese nivel superior de rendimiento es poner atención en las pruebas de rendimiento. Hace mucho que noté que la gente habla mucho sobre técnicas para mejorar el rendimiento, pero lo único que realmente marca la diferencia es probarlo.
Fowler mencionó que se encontraron varias cosas, pero solo mencionó un par.
¿Hay otras arquitecturas, bibliotecas, técnicas o "cosas" que sean útiles para alcanzar esos niveles de rendimiento?
fuente
Respuestas:
Hay todo tipo de técnicas para el procesamiento de transacciones de alto rendimiento y la que se encuentra en el artículo de Fowler es solo una de las más avanzadas. En lugar de enumerar un montón de técnicas que pueden o no ser aplicables a la situación de cualquier persona, creo que es mejor discutir los principios básicos y cómo LMAX aborda una gran cantidad de ellos.
Para un sistema de procesamiento de transacciones a gran escala, desea hacer todo lo siguiente tanto como sea posible:
Minimice el tiempo que pasa en los niveles de almacenamiento más lentos. De más rápido a más lento en un servidor moderno que tiene: CPU / L1 -> L2 -> L3 -> RAM -> Disco / LAN -> WAN. El salto desde incluso el disco magnético moderno más rápido a la RAM más lenta es más de 1000x para acceso secuencial ; El acceso aleatorio es aún peor.
Minimice o elimine el tiempo de espera . Esto significa compartir el menor estado posible y, si el estado debe compartirse, evitar bloqueos explícitos siempre que sea posible.
Difundir la carga de trabajo. Las CPU no se han vuelto mucho más rápidas en los últimos años, pero se han vuelto más pequeñas y 8 núcleos son bastante comunes en un servidor. Más allá de eso, incluso puede distribuir el trabajo en varias máquinas, que es el enfoque de Google; Lo mejor de esto es que escala todo, incluidas las E / S.
Según Fowler, LMAX adopta el siguiente enfoque para cada uno de estos:
Mantenga todo el estado en la memoria en todo momento. La mayoría de los motores de bases de datos lo harán de todos modos, si toda la base de datos puede caber en la memoria, pero no quieren dejar nada al azar, lo cual es comprensible en una plataforma de negociación en tiempo real. Para lograr esto sin agregar un montón de riesgos, tuvieron que construir un montón de infraestructura de respaldo y conmutación por error liviana.
Use una cola sin bloqueo ("disruptor") para la secuencia de eventos de entrada. Contraste con las colas de mensajes duraderas tradicionales que definitivamente no están libres de bloqueo y, de hecho, generalmente implican transacciones distribuidas dolorosamente lentas .
No mucho. LMAX lanza este debajo del bus sobre la base de que las cargas de trabajo son interdependientes; el resultado de uno cambia los parámetros para los otros. Esta es una advertencia crítica , y que Fowler llama explícitamente. Hacen alguna uso de concurrencia con el fin de proporcionar capacidades de conmutación por error, pero toda la lógica de negocio se procesa en un solo hilo .
LMAX no es el único enfoque para OLTP a gran escala. Y aunque es bastante brillante por derecho propio, no es necesario utilizar técnicas de vanguardia para lograr ese nivel de rendimiento.
De todos los principios anteriores, el # 3 es probablemente el más importante y el más efectivo, porque, francamente, el hardware es barato. Si puede dividir adecuadamente la carga de trabajo en media docena de núcleos y varias docenas de máquinas, entonces el cielo es el límite para las técnicas convencionales de computación paralela . Te sorprendería la cantidad de rendimiento que puedes lograr con nada más que un montón de colas de mensajes y un distribuidor de turnos. Obviamente, no es tan eficiente como LMAX, en realidad ni siquiera cercano, pero el rendimiento, la latencia y la rentabilidad son preocupaciones separadas, y aquí estamos hablando específicamente sobre el rendimiento.
Si tiene el mismo tipo de necesidades especiales que LMAX tiene, en particular, un estado compartido que corresponde a una realidad empresarial en lugar de una elección de diseño apresurada, entonces sugeriría probar su componente, porque no he visto mucho De lo contrario, es adecuado para esos requisitos. Pero si simplemente estamos hablando de alta escalabilidad, entonces le insto a que investigue más sobre los sistemas distribuidos, porque son el enfoque canónico utilizado por la mayoría de las organizaciones hoy en día (Hadoop y proyectos relacionados, ESB y arquitecturas relacionadas, CQRS que Fowler también menciona, y así sucesivamente).
Los SSD también se convertirán en un cambio de juego; posiblemente, ya lo son. Ahora puede tener almacenamiento permanente con tiempos de acceso similares a la RAM, y aunque los SSD de nivel de servidor siguen siendo terriblemente caros, eventualmente bajarán de precio una vez que aumenten las tasas de adopción. Se ha investigado ampliamente y los resultados son bastante alucinantes y solo mejorarán con el tiempo, por lo que todo el concepto de "mantener todo en la memoria" es mucho menos importante de lo que solía ser. Entonces, una vez más, trataría de centrarme en la concurrencia siempre que sea posible.
fuente
Creo que la mayor lección para aprender de esto es que debes comenzar con lo básico:
Durante las pruebas de rendimiento, puede perfilar su código, encontrar los cuellos de botella y corregirlos uno por uno.
Demasiadas personas saltan directamente a la parte "arreglarlas una por una". Pasan mucho tiempo escribiendo "implementaciones personalizadas de las colecciones de Java", porque saben que la razón por la cual su sistema es lento es debido a errores de caché. Eso puede ser un factor contribuyente, pero si saltas directamente a modificar código de bajo nivel como ese, es probable que te pierdas el problema más grande de usar una ArrayList cuando deberías usar una LinkedList, o que la verdadera razón es que tu sistema es lento es porque su ORM está cargando de manera lenta a los hijos de una entidad y, por lo tanto, realiza 400 viajes separados a la base de datos para cada solicitud.
fuente
No comentaré particularmente sobre el código LMAX porque creo que está ampliamente descrito, pero aquí hay algunos ejemplos de cosas que he hecho que han resultado en mejoras significativas de rendimiento medibles.
Como siempre, estas son técnicas que deben aplicarse una vez que sepa que tiene un problema y necesita mejorar el rendimiento ; de lo contrario, es probable que solo esté haciendo una optimización prematura.
Ayudar al compilador JIT con la finalización de campos, métodos y clases finales permite optimizaciones específicas que realmente ayudan al compilador JIT. Ejemplos específicos:
Reemplace las clases de colección con matrices : esto da como resultado un código menos legible y es más difícil de mantener, pero casi siempre es más rápido, ya que elimina una capa de indirección y se beneficia de muchas optimizaciones agradables de acceso a la matriz. Por lo general, una buena idea en los bucles internos / código sensible al rendimiento después de haberlo identificado como un cuello de botella, ¡pero evite lo contrario por razones de legibilidad!
Utilice primitivas siempre que sea posible : las primitivas son fundamentalmente más rápidas que sus equivalentes basados en objetos. En particular, el boxeo agrega una gran cantidad de sobrecarga y puede causar pausas desagradables en el GC. No permita que se empaqueten primitivas si le preocupa el rendimiento / la latencia.
Minimice el bloqueo de bajo nivel : los bloqueos son muy caros a un nivel bajo. Encuentre formas de evitar el bloqueo por completo, o bloquee en un nivel de grano grueso para que solo necesite bloquear con poca frecuencia en grandes bloques de datos y el código de bajo nivel pueda continuar sin tener que preocuparse por los problemas de bloqueo o concurrencia.
fuente
final
algunos JIT podrían resolverlo, otros podrían no. Depende de la implementación (al igual que muchos consejos de ajuste de rendimiento). Acuerde las asignaciones: debe comparar esto. Por lo general, he descubierto que es mejor eliminar las asignaciones, pero YMMV.Aparte de lo que ya se indicó en una excelente respuesta de Aaronaught, me gustaría señalar que un código como ese podría ser bastante difícil de desarrollar, comprender y depurar. "Si bien es muy eficiente ... es muy fácil fastidiar ..." como uno de sus muchachos mencionó en el blog de LMAX .
Dado lo anterior, creo que aquellos que eligen Disruptor y enfoques similares se aseguran mejor de que tengan recursos de desarrollo suficientes para mantener su solución.
En general, el enfoque disruptivo me parece bastante prometedor. Incluso si su empresa no puede permitirse el lujo de utilizarlo, por ejemplo, por los motivos mencionados anteriormente, considere convencer a su gerencia para que "invierta" algo de esfuerzo en estudiarlo (y SEDA en general), porque si no lo hacen, existe la posibilidad de que algún día sus clientes los dejarán a favor de alguna solución más competitiva que requiera 4x, 8x, etc. menos servidores.
fuente