La optimización real del sistema de comercio electrónico con tráfico de miles de millones

Clasificación de ajuste de JVM

El tuning es un gran concepto, en pocas palabras, es optimizar el sistema. (Suponiendo que no hay ningún problema con el código, configure los parámetros para que el programa se ejecute como esperamos (¿alto rendimiento? ¿Baja latencia?))

Pero desde la perspectiva de un sistema, hay demasiadas cosas que se pueden hacer. Generalmente dividimos el ajuste de JVM en las siguientes tres categorías:

  1. Ajuste previo de JVM
  2. Optimice el entorno operativo de la JVM (lento, retrasado, etc.)
  3. Resolver problemas en la JVM (OOM)

En tuning, el más obvio es OOM. Porque lanzará una excepción. Por supuesto, es solo una parte de la afinación. El ajuste previo y la optimización del entorno operativo estiman que las prácticas de muchas personas son solo reinicios del servidor. Usamos la herramienta AB para medir la presión ( detallado en mi otro blog JVM tuning-memory optimization ), de hecho, lo principal es preajustar y optimizar el entorno operativo. Permítanme resumir aquí.

Ajuste previo de JVM

Configuración de escenario empresarial

El tuning se divide en escenarios. Así que asegúrese de aclarar la configuración de la escena del proyecto. Como ahora, todo el mundo es una arquitectura de microservicio. Una vez dividido el servicio, es más adecuado para la configuración de la escena.

Por ejemplo, esta parte del servicio se centra en el rendimiento y la otra parte se centra en la experiencia del usuario (tiempo de respuesta del usuario), etc.

Sin monitoreo, sin optimización

El seguimiento aquí se refiere a las pruebas de estrés. Puedes ver los resultados y hay datos. No optimices sintiendo. Todo debe tener indicadores cuantitativos. Como rendimiento, tiempo de respuesta, recursos del servidor, recursos de red, etc. En una palabra: sin supervisión, sin optimización.

Pasos de procesamiento

Requisitos de memoria de la computadora

Calcule los requisitos de memoria. La memoria no es tan grande como sea posible. Para los sistemas generales, la demanda de memoria es elástica. La memoria es pequeña y la velocidad de recuperación es rápida y puede soportarla. Por lo tanto, no existe una especificación fija para el tamaño de la memoria. El tamaño de la pila de la máquina virtual puede reducirse en condiciones de alta concurrencia. (1 M no se puede utilizar, guarde la memoria para el montón o el metaespacio).

El metaespacio (área de método) aún debe establecer un valor máximo (por defecto, el metaespacio no tiene límite de tamaño), generalmente unos pocos cientos de M son suficientes. ¿Por qué también limitar el metaespacio?

Dado que ajustar el tamaño del metaespacio requiere GC completo, esta es una operación muy costosa. Si se produce una gran cantidad de GC completo cuando se inicia la aplicación, generalmente se debe a la generación permanente o al ajuste de tamaño del metaespacio. En base a esta situación, generalmente se recomienda En los parámetros de JVM, establecer MetaspaceSize y MaxMetaspaceSize en el mismo valor, y establecerlo para que sea mayor que el valor inicial ( cuando se ejecuta un programa, el tamaño del metaespacio generalmente no cambia , o el cambio es pequeño) , para la memoria física de 8G Para la máquina, generalmente establezco ambos valores en 256M (PS: los lectores pueden ajustarlo de acuerdo con su situación real).

Seleccionar CPU

Para el sistema, cuanto mayor sea el rendimiento de la CPU, mejor. Esto se determina de acuerdo con el presupuesto (la CPU es muy cara).

Especialmente ahora que el servidor está virtualizado, el índice de rendimiento de la máquina virtual no solo puede mirar el índice de parámetros después de la virtualización, sino también la situación de la máquina física.

Permítanme hablarles sobre un ejemplo de lo que le sucedió al maestro Xiang Xueking en el aula de Tencent: antes de usar 8 servidores para equilibrar la carga. Las máquinas en ese momento llegaron en dos lotes. El primer lote de 4 computadoras realizó pruebas de presión en el programa sin ningún problema. Pero después de que llegue el segundo lote de 4 computadoras, 8 computadoras se atascarán cuando ejecuten el programa juntas. Al final, se encontró la razón: los parámetros de la máquina virtual de las últimas cuatro máquinas estaban a la altura del estándar, pero después de encontrar la máquina física, se descubrió que el rendimiento estaba muy por debajo de la máquina anterior.

Elegir el recolector de basura adecuado

Para el escenario de rendimiento primero, solo hay una opción, que es usar la combinación PS (Paraller Scavenge + Parallel Old) (¿Hay una pregunta como yo, por qué no es una combinación PP, sino una combinación PS?) . Aquí están los puntos de conocimiento nuevamente. Todos decimos que CMS es un recolector de basura que persigue el rendimiento. ¿Por qué existe una combinación de PS?

De hecho, la combinación de PS es el límite real para lograr el rendimiento. Hizo todo lo posible cuando estaba trabajando y cuando estaba barriendo la basura. Por lo tanto, su velocidad de recolección de basura es más eficiente. Es solo que usa demasiada fuerza bruta y toda la recolección de basura es STW. Por tanto, aunque es rápido, puede provocar un largo período de retraso en el escenario empresarial de la experiencia del usuario.

Para escenarios donde se prioriza el tiempo de respuesta, G1 tiene prioridad en JDK1.8, seguido por el recolector de basura CMS.

Establezca el tamaño de la generación joven y la edad de las generaciones.

Como se explica en mi otro artículo Optimización de memoria de ajuste de JVM :

Aplicaciones de rendimiento primero: en general, las aplicaciones de rendimiento primero deben tener una generación joven más grande y una generación anterior más pequeña. La razón es que la mayoría de los objetos a corto plazo se pueden reciclar tanto como sea posible, y los objetos a medio plazo se pueden reducir, mientras que la vejez puede almacenar los objetos supervivientes a largo plazo tanto como sea posible.

Establecer parámetros de registro

-XX: + Registro de GC de salida PrintGC

-XX: + PrintGCDetails muestra el registro detallado de GC

-XX: + PrintGCTimeStamps marca de tiempo del GC de salida (en forma de tiempo base)

-XX: + PrintGCDateStamps marca de tiempo del GC de salida (por ejemplo: 2013-05-04T21: 53: 59.234 + 0800)

-XX: + PrintHeapAtGC imprime información del montón antes y después de GC

-Xloggc: ../ logs / gc.log ruta de salida del archivo de registro

Nota: Generalmente, si solo hay un archivo de registro, no funcionará. A veces, el archivo de registro generado por un proyecto de alta concurrencia en un día tendrá una T. De hecho, el registro debe ser una cuestión de operación y mantenimiento. Los archivos de registro nos ayudan a analizar el problema

Optimice el entorno operativo de la JVM (lento, retrasado, etc.)

Generalmente, la causa del retraso o lentitud de JVM no es más que dos partes:

  1. El uso de la CPU es demasiado alto.
  2. El uso de memoria es demasiado alto.

Por lo tanto, necesitamos analizar temas específicos en detalle, y podemos pensar e investigar desde estos dos aspectos.

Resolver problemas en JVM (OOM, etc.)

Desbordamiento de pila

El tamaño del marco de la pila en la versión HotSpot es fijo y no admite la expansión.

java.lang.StackOverflowError La llamada al método general es muy difícil de aparecer, si lo hace, puede ser una recursividad infinita.

La iluminación de la pila de la máquina virtual: debido a que la ejecución del método está empaquetada en un marco de pila, es inherentemente más lenta que el ciclo que implementa la misma función. Por lo tanto, en el algoritmo de recorrido de árbol: recursivo y no recursivo (implementado por bucle) existe significado. El código recursivo es conciso, el código no recursivo es complejo pero más rápido.

OutOfMemoryError: los subprocesos se crean continuamente, la JVM se aplica a la memoria de pila y la máquina no tiene suficiente memoria.

Al mismo tiempo, debe tenerse en cuenta que no hay forma de limitar el espacio de la JVM en el área de la pila, porque la JVM tendrá subprocesos ejecutándose continuamente durante el proceso en ejecución, y no hay forma de limitarlo, así que solo el tamaño de la pila de una sola máquina virtual es limitado.

Desbordamiento del montón

Desbordamiento de memoria: la aplicación de espacio de memoria excede el espacio máximo de memoria del montón.

Si se trata de una pérdida de memoria, verifique su propio código.

Si la memoria se desborda, aumente los parámetros -Xms y -Xmx.

Si no es una fuga de memoria, es decir, todos los objetos en la memoria deben sobrevivir, entonces debe verificar la configuración de los parámetros del montón de la JVM durante mucho tiempo, compararla con la memoria de la máquina, para ver si hay espacio. para el ajuste, y luego verifique el código para ver si hay algunas situaciones en las que el ciclo de vida de algunos objetos es demasiado largo, el tiempo de estado de retención es demasiado largo y el diseño de la estructura de almacenamiento no es razonable, para minimizar el consumo de memoria cuando el programa se está ejecutando.

Desbordamiento del área de método

(1) Desbordamiento constante de la piscina durante el tiempo de ejecución

(2) El objeto Clase guardado en el área de método no se recicla a tiempo o

Desbordamiento de memoria nativa

La capacidad de la memoria directa se puede configurar mediante MaxDirectMemorySize (el valor predeterminado es el mismo que la memoria dinámica máxima), por lo que también se producirán excepciones OOM;

Una característica más obvia del desbordamiento de memoria causado por la memoria directa es que no se observarán anomalías obvias en el archivo HeapDump. Si se produce OOM y el archivo de volcado es pequeño, puede considerar la posibilidad de investigar la causa de la memoria directa.

Ajuste de JVM del sistema de comercio electrónico de tráfico de mil millones

Sistema de flujo de miles de millones de niveles

 

El sistema de tráfico de mil millones de niveles es en realidad un sistema con cientos de millones de visitas por día. Según el análisis de datos oficiales de Taobao. Cada usuario navega y hace clic de 20 a 40 veces a la vez, y se estima que cada usuario activo (usuario activo diario) es de aproximadamente 5 millones. Al mismo tiempo, combinado con los datos de un clic de Taobao, se puede encontrar que solo alrededor del 10% de los usuarios pueden pagar.
El 90% de los usuarios solo están navegando, luego podemos usar la caché de imágenes, la caché de Redis, podemos resolver el 90% de los usuarios.

 

Para el 10% de los usuarios que pagan, se calcula aproximadamente que hay alrededor de 500.000 transacciones por día.

Estimación de GC

Si se trata de un negocio ordinario, el tiempo de procesamiento es generalmente relativamente plano, alrededor de 3 a 4 horas. Se calcula que solo hay decenas de pedidos por segundo. Este servidor general no realiza ningún procesamiento (no es necesario realizar estimaciones ni ajustes). Además, existen grandes escenarios de promoción en el sistema de comercio electrónico (seckill, prisa por tiempo limitado, etc.). Generalmente este tipo de negocios se concentra en pocos minutos. Calculamos alrededor de 2000 pedidos por segundo.

Utilice 4 servidores (con equilibrio de carga) para grandes escenarios de promoción. Cada servidor de pedidos tiene aproximadamente 500 pedidos por segundo.

Nuestra prueba encontró que cada proceso de procesamiento de pedidos ocupará 0,2 MB de espacio (información del pedido, cupones, información de pago, etc.). Entonces, un servidor genera 500 * 0.2 = 100M de espacio de memoria por segundo. Estos objetos son inútiles después de ser procesados, es decir, pertenecen a los objetos de la vida y la muerte. Se convertirá en basura después de 1 s.

 

Únase a nosotros para establecer el espacio de pila máximo en 3G, lo configuramos de forma predeterminada, 1/3 del espacio de pila de la generación joven y 2/3 del espacio de pila de la generación anterior. Edem: Desde: hasta = 8: 1: 1.

Inferimos que el área antigua = 2G, el área del Edén = 800M, S0 = S1 = 100M

De acuerdo con el principio de asignación de objetos (los objetos se asignan primero en el área del Edén), se puede obtener que el área del Edén esté llena en aproximadamente 8 segundos.

Un MinorGC (recolección de basura de nueva generación) se activa cada 8 segundos. Esta vez, durante MinorGC, la JVM requiere STW, pero en este momento, hay 100 millones de objetos que no se pueden recuperar (los subprocesos se suspenden y los objetos se convertirán en objetos basura después de 1 segundo. Por lo tanto, esto La colección de objetos no reciclables dentro de 1 s es 100M), luego habrá 100M de objetos que no se pueden reciclar esta vez (solo la próxima vez que se puedan reciclar)

Entonces, después de esta recolección de basura. Los 100M de objetos que sobrevivan esta vez entrarán en el área S0, pero debido a otro principio de asignación de objetos de JVM (si el tamaño total de todos los objetos de la misma edad en el espacio Survivor es mayor que la mitad del espacio Survivor, los objetos con una edad mayor mayor o igual a esta edad puede ingresar directamente En la vejez, no hay necesidad de esperar hasta la edad requerida en MaxTenuringThreshold)

Por lo tanto, tales objetos no ingresarán al área de Supervivientes, sino que ingresarán a la vejez.

 

 

Por lo tanto, estimamos que alrededor de 100 millones de objetos entrarán en la vejez cada 8 segundos. Aproximadamente 20 * 8 = 160 segundos, es decir, el área anterior se llenará en aproximadamente 2 minutos y 40 segundos, y se activará un FullGC. En términos generales, esta vez se puede evitar el FullGC. Al mismo tiempo, porque FullGC lo hace no solo reciclar la generación anterior + la nueva generación Durante generaciones, el metaespacio debe ser recuperado. Estos FullGC pueden llevar mucho tiempo (para los objetos que se recuperan en la vejez, el uso de algoritmos de clasificación de barrido / marcado de etiquetas determina que la eficiencia no es alta. Al mismo tiempo, el metaespacio debe recuperarse una vez. Aumente el tiempo de GC). Entonces, la raíz del problema es cómo evitar FullGC innecesario.

Ajuste de GC

Agregamos parámetros de VM al proyecto:

-Xms3072M -Xmx3072M -Xmn2048M -XX: SurvivorRatio = 7

-Xss256K -XX: MetaspaceSize = 128M -XX: MaxMetaspaceSize = 128M

-XX: MaxTenuringThreshold = 2

-XX: ParallelGCThreads = 8

-XX: + UseConcMarkSweepGC

Primero mire el espacio del montón: área antigua = 1G, área del Edén = 1.4G, S0 = S1 = 300M

 

Luego, el primer punto, el área del Edén tarda unos 14 segundos en llenarse. Una vez que se llena, 100 millones de objetos supervivientes entrarán en el área del formulario (debido a que esta área se agranda en 300/2 = 150 millones> 100 millones, la determinación dinámica de la edad no motivado)

 

Después de otros 14 segundos, el área del Edén se llenará y todavía quedan 100 millones de objetos para ingresar al área S1. Sin embargo, dado que el 100M original ya es basura (han pasado 14 segundos), S1 solo tendrá 100M de objetos del área de Eden, y los 100M de S0 no se han reciclado y no se activará la determinación dinámica de la edad.

 

 

Repetidamente, para que ningún objeto entre en la zona anterior y no se active FullGC. Al mismo tiempo, la frecuencia de nuestro MinorGC ha cambiado de los 8 segundos anteriores a 14 segundos. Aunque el espacio se agranda, el tiempo total de GC se obtiene a cambio. Reducirá

Generalmente, el espacio no cambiará mucho después del inicio, podemos configurarlo en 128M para ahorrar espacio en la memoria.

-Xss256K -XX: MetaspaceSize = 128M -XX: MaxMetaspaceSize = 128M. Las pilas de máquinas virtuales rara vez usan 1M en general. Entonces, para que los subprocesos ocupen menos memoria, podemos reducirlo a un espacio de yuanes de 256K. Generalmente, no habrá demasiados cambios después del inicio. Podemos configurarlo en 128M para ahorrar espacio de memoria. -XX: MaxTenuringThreshold = 2 Esta es la edad generacional (puede ingresar a la vejez cuando tenga 2 años), porque básicamente usamos la arquitectura Spring. Muchos beans en Spring sobrevivirán durante mucho tiempo. No es necesario hacer la transición en el área de Supervivientes. Es demasiado larga, por lo que puede establecerlo en 2 para que la mayoría de los objetos internos de Spring entren en la vejez. (Los objetos que viven y mueren no pueden durar una ronda ) -XX: ParallelGCThreads = 8 El número de subprocesos se puede configurar de acuerdo con la situación de los recursos de su servidor (si desea una velocidad rápida, puede establecer un punto más grande, dependiendo de la situación de la CPU, generalmente configurada en CPU ( este escenario es un escenario de uso intensivo de CPU ). -XX: + UseConcMarkSweepGC Debido a que se prioriza el tiempo de respuesta de este negocio, aún es posible usar el recolector de basura CMS o el recolector de basura G1.

 

 

 

 

 

 

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/weixin_47184173/article/details/110607760
Recomendado
Clasificación