Resumen de los temas de diseño de sistemas tres

Algunas expansiones y optimizaciones del sistema 20 seckill

20.1 Cuando envía un mensaje, el proceso es enviar el mensaje a MQ para un procesamiento asincrónico, y luego el consumidor consume el mensaje y luego llama a la interfaz de envío de mensajes del operador. ¿Qué sucede si el mensaje no se envía después de llamar a la interfaz del operador? ?

De hecho, para este proceso comercial central, especialmente cuando se trata de integración a nivel de operador, también podemos considerar las siguientes soluciones más refinadas o más completas:

  1. Modo de fusible :

    • Cuando la falla continua de la interfaz del operador alcanza un cierto umbral, el fusible se habilita y la llamada al operador se detiene temporalmente. Esto ayuda a proteger los sistemas y los operadores de verse abrumados por un flujo constante de solicitudes fallidas.
    • El disyuntor entra en un estado "medio abierto" después de un período de tiempo, lo que permite un número limitado de solicitudes para intentar llamar a la interfaz del operador. Si estas solicitudes tienen éxito, el disyuntor se cierra y se reanuda el funcionamiento normal; si sigue fallando, el disyuntor permanece abierto.
  2. Estrategia de reserva :

    • Considere usar otro método de entrega de respaldo cuando la interfaz del operador no esté disponible. Este podría ser otro operador, o una transmisión de mensaje completamente diferente (por ejemplo, correo electrónico, mensajería en el sitio, etc.).
  3. cola de prioridad :

    • En algunos casos, algunos mensajes pueden ser más urgentes que otros. Use una cola de prioridad para asegurarse de que los mensajes urgentes se envíen primero.
  4. Estrategias antiestrés :

    • Si la tasa de envío de mensajes excede la capacidad de procesamiento del sistema o del operador, la tasa de envío de mensajes debe reducirse, lo que se denomina estrategia de "contrapresión".
  5. Confirmación y comprobación de mensajes :

    • Si es posible, se puede solicitar al operador que devuelva un acuse de recibo de la entrega del mensaje. De esta forma, si no se recibe confirmación, es seguro volver a intentarlo, sabiendo que el intento anterior no tuvo éxito.
  6. Control de flujo de negocios :

    • En algunos casos, puede ser necesario detener o limitar temporalmente la generación de nuevos mensajes hasta que se resuelva el problema.
  7. Configuración dinámica y conmutación :

    • Proporciona una capacidad de conmutación dinámica para múltiples transportistas o métodos de entrega. De esta manera, cuando ocurre un problema con un método, los administradores del sistema pueden cambiar rápidamente a otro método sin volver a implementar o reiniciar el sistema.

Finalmente, independientemente de la estrategia que elija, debe realizar suficientes pruebas para simular varias posibles situaciones de falla para garantizar que el sistema pueda ejecutarse de manera sólida en el entorno real.

20.2 Al comprar productos, si hay una alta concurrencia, entonces de acuerdo con su lógica en este momento, cada vez que un usuario compra un producto, se insertan los datos del pedido y se bloquea el inventario. ¿Qué medidas de optimización hay?

Mi respuesta es: el final de redis primero disminuye previamente el inventario y luego usa un MQ, que se usa especialmente para almacenar la lógica de compra del usuario. Siempre que el MQ esté lleno, se enviará a MySQL al mismo tiempo para el almacenamiento de datos.

20.3 Continuando con la pregunta anterior, si muchos usuarios compran el mismo producto, habrá un problema de bloqueo de fila cuando bloquee el inventario en lotes en este momento, por lo que los datos del usuario solo se pueden eliminar uno por uno. esta vez?

Respuesta: A solicitud del entrevistador, respondí para dividir los datos de inventario del producto. Por ejemplo, para el mismo producto, cada región inserta datos de inventario. Registros de inventario, para que los usuarios del sur elijan deducir el inventario en Shanghái. y los usuarios en el norte eligen deducir el inventario en Beijing.Aunque es el mismo producto, debido a que hay dos registros de inventario, puede compartir la presión de la concurrencia

Su respuesta es una buena estrategia, es decir, reducir efectivamente la contención de bloqueo en un solo registro al dividir el inventario geográficamente. La clave de este método es subdividir los datos del punto de acceso para lograr el efecto de equilibrio de carga.

20.3.1 Este enfoque tiene las siguientes ventajas:

  1. Reduzca la contención por bloqueos : como dijo, diferentes regiones geográficas pueden operar en diferentes registros de inventario, lo que reduce el acceso simultáneo al mismo registro de inventario.

  2. Capacidad de respuesta mejorada : los usuarios pueden obtener respuestas más rápido debido a la menor contención de bloqueos.

  3. Mejor escalabilidad : cuando aumenta la cantidad de usuarios en un área determinada, el inventario en esta área se puede subdividir aún más, por ejemplo, subdividiendo Beijing en Dongcheng, Xicheng, etc.

  4. Gestión de inventario en tiempo real : para las plataformas de comercio electrónico, este método también puede ajustar el inventario en tiempo real. Por ejemplo, cuando el inventario en Shanghai es insuficiente y el inventario en Beijing es suficiente, puede considerar transferir el inventario de Beijing a Shanghai.

20.3.2 Sin embargo, esta estrategia tiene algunos problemas potenciales:

  1. Mayor complejidad de la gestión de datos : los datos de inventario deben gestionarse para cada región, lo que puede conducir a una gestión de datos más compleja.

  2. Problemas de sincronización de inventario : por ejemplo, ¿cómo se asegura de que los datos de inventario sean correctos cuando los artículos se mueven de una región a otra?

  3. Pregunta sobre la experiencia del usuario : ¿Qué sucede si un usuario quiere comprar más de lo que está en stock en su región, pero hay mucho stock en otras regiones?

En resumen, dividir el inventario en regiones geográficas es una buena estrategia para reducir la competencia de cerraduras, pero al mismo tiempo, también es necesario considerar los problemas potenciales mencionados anteriormente y brindarles soluciones.

20.4 Continuando con la pregunta anterior, si la división del inventario aquí no se divide según la región, sino que se divide directamente en varios grupos, cada grupo tiene la misma cantidad de inventario y luego viene la solicitud de equilibrio de carga temporal. se completa la deducción, se cerrará en esta línea Enrutamiento de datos, por lo que no es necesario ajustar el inventario a otros grupos, ¿verdad?

Sí, la estrategia que mencionó también es factible. Al dividir el inventario en varios grupos y establecer la misma cantidad de inventario en cada grupo, se puede realizar el equilibrio de carga cuando llegan las solicitudes. La ventaja de este enfoque es que reduce la competencia de bloqueo y mejora la concurrencia del sistema. Sin embargo, hay algunas cosas a considerar al implementar esta estrategia:

  1. División y fusión de inventario : después de dividir el inventario en varios grupos, es necesario establecer la misma cantidad de inventario en cada grupo. Al mismo tiempo, es necesario diseñar un mecanismo adecuado para que cuando el inventario de un determinado grupo sea insuficiente, el inventario de otros grupos pueda fusionarse en el tiempo, para evitar el desperdicio de inventario causado por el inventario insuficiente de un cierto grupo.

  2. Sincronización de inventario : en escenarios de alta concurrencia, varios subprocesos accederán y modificarán el inventario de cada grupo al mismo tiempo. Es necesario asegurarse de que el acceso y la modificación del inventario por parte de cada subproceso se pueda sincronizar correctamente.

  3. Algoritmo de equilibrio de carga : es necesario seleccionar un algoritmo de equilibrio de carga adecuado para distribuir las solicitudes a cada grupo de inventario. Aquí puede elegir algoritmos simples de sondeo, aleatorios y de otro tipo, o algoritmos más complejos de equilibrio de carga basados ​​en el peso.

  4. Coherencia de los datos : si los datos del inventario se almacenan en varias bases de datos, es necesario considerar los problemas de coherencia de los datos. Aquí puede utilizar métodos como la confirmación en dos fases y la coherencia final para garantizar la coherencia de los datos.

Esta estrategia puede reducir efectivamente la competencia de bloqueo y mejorar la concurrencia del sistema. Sin embargo, los problemas antes mencionados deben tenerse plenamente en cuenta al implementar esta estrategia, y la optimización y el ajuste deben realizarse de acuerdo con la situación real.

21 ¿Cómo asegura rabbitmq que los mensajes no se pierdan?

En RabbitMQ, hay varias formas de garantizar que los mensajes no se pierdan:

  1. Cola de persistencia :

    • Al declarar una cola, hágala persistente. De esta forma, incluso si el servidor RabbitMQ se reinicia, los mensajes en la cola no se perderán. Sin embargo, esto no garantiza que los mensajes se escribirán correctamente en el disco hasta que se reinicie el servidor.
    • En el cliente de Java, puede crear una cola persistente como esta:channel.queueDeclare("queue-name", true, false, false, null);
  2. Persistencia del mensaje :

    • Configure el mensaje para que sea persistente, de modo que incluso si el servidor RabbitMQ se reinicia, el mensaje no se perderá. Sin embargo, esto no garantiza que los mensajes se escribirán correctamente en el disco hasta que se reinicie el servidor.
    • En el cliente Java, puede publicar mensajes persistentes como este:channel.basicPublish("", "queue-name", MessageProperties.PERSISTENT_TEXT_PLAIN, "message".getBytes());
  3. Mecanismo de confirmación del remitente :

    • El remitente puede asegurarse de que el mensaje se haya enviado correctamente al servidor RabbitMQ configurando el mecanismo de confirmación. Después de que el mensaje se envíe con éxito al servidor, el remitente recibirá un mensaje de confirmación.
    • En el cliente Java, el mecanismo de reconocimiento del remitente se puede configurar de la siguiente manera:channel.confirmSelect();
  4. Mecanismo de confirmación del receptor :

    • El receptor puede asegurarse de que el mensaje se haya procesado con éxito configurando un mecanismo de reconocimiento. Una vez que el mensaje se procesa con éxito, el receptor envía un reconocimiento al servidor RabbitMQ.
    • En el cliente Java, el mecanismo de confirmación del receptor se puede configurar de la siguiente manera:channel.basicAck(deliveryTag, false);
  5. Despliegue de clúster y cola de espejo :

    • Al implementar RabbitMQ como un clúster y configurar colas reflejadas, puede asegurarse de que, incluso si falla un nodo, los mensajes no se perderán.

Cabe señalar que el método anterior puede afectar el rendimiento de RabbitMQ. Por ejemplo, habilitar el mecanismo de persistencia y confirmación de mensajes aumentará la operación de IO y la transmisión de red de RabbitMQ. Por lo tanto, al elegir un método apropiado para garantizar que los mensajes no se pierdan, debe sopesarse de acuerdo con los escenarios de aplicación específicos y los requisitos de rendimiento.

22 Cómo resolver la situación en la que el desplazamiento del mapa de bits en Redis es demasiado grande y se desperdicia el espacio

Me gustaría hacerle una pregunta. Ahora hay un escenario en el que necesita contar la información de actividad diaria de cada dispositivo. El número del dispositivo tiene la forma de ID de Snowflake. En este momento, quiero usar el mapa de bits de Redis para el almacenamiento de datos, pero hay un problema, si el primer día, un dispositivo se conectó y calculó el desplazamiento en 1, y estableció el bit especificado del mapa de bits en 1, y luego el desplazamiento calculado por la identificación de otro dispositivo superó el límite máximo del mapa de bits, lo que provocó que redis abriera directamente 512 M de memoria, ¿cómo evitar que el desplazamiento sea demasiado grande? Además, si la compensación calculada por un dispositivo es 1 y la compensación calculada por el otro dispositivo es 100000, y solo 2 dispositivos están realmente en línea el primer día, entonces 2-99999 es un espacio no válido y provoca una pérdida de memoria. ¿Cómo se puede solucionar esto? Sé que EWAHCompressedBitmap de Google se puede comprimir, pero esto solo se puede usar en una sola instancia

22.1 Idea 1

Dame una idea: supongamos que se usa un número de serie para identificar el orden de la identificación del dispositivo en el mapa de bits original, ahora los primeros dígitos x de esta identificación se usan como el identificador de mapa de bits, y los últimos dígitos se usan como su posición en el mapa de bits i-th, y luego la estrategia de carga perezosa, solo cuando se usa el dispositivo, se crea el mapa de bits donde se encuentra el dispositivo, para hacer frente a situaciones extremas.
Por ejemplo, ahora hay 100 identificadores de dispositivos, numerados del 0 al 99, que deben almacenarse en un mapa de bits con un tamaño de bits de 100. Suponga que se divide en 20 mapas de bits, y cada mapa de bits solo es responsable de contar la información activa. de 5 dispositivos Los dispositivos No. 1 y No. 99 están en línea, por lo que solo necesita crear dos mapas de bits No. 0 y No. 19, y el consumo total de memoria es de 10 bits, el proceso de cálculo toma el dispositivo con la identificación de 99 como ejemplo: el dispositivo está en el 99/ 5=19 bits en el mapa de bits cuyo índice es 99% 5=4 están marcados en el bit

22.2 Idea 2:

¿Es necesario contar las actividades diarias de todos los dispositivos?¿Se pueden agrupar los dispositivos y luego contar la información activa de cada grupo de dispositivos, lo que también puede comprimir el espacio?

22.3 Usando el mapa de bits rugiente directamente

Roaring Bitmap es una implementación de mapa de bits eficiente, que es más compacta que el mapa de bits tradicional y es adecuada para grandes cantidades de datos. Funciona bastante bien en muchos escenarios, como big data, almacén de datos, motor de búsqueda y otros escenarios. La idea central de Roaring Bitmap es dividir el mapa de bits en múltiples contenedores y usar diferentes estructuras de datos para el almacenamiento de acuerdo con la cantidad de 1 en el contenedor, ahorrando así espacio.

Las características de Roaring Bitmap incluyen:

  1. Almacenamiento eficiente : al elegir inteligentemente las estructuras de datos, Roaring Bitmap puede almacenar datos de manera eficiente. Por ejemplo, cuando los 1 en un contenedor son muy densos, Roaring Bitmap elegirá una estructura de datos compacta para el almacenamiento, ahorrando así espacio.

  2. Operación rápida : Roaring Bitmap proporciona una serie de operaciones rápidas, como unión de bits, intersección, diferencia, volteo, etc. Estas operaciones están altamente optimizadas y se pueden completar en muy poco tiempo.

  3. Amplia aplicación : debido a su alta eficiencia y flexibilidad, Roaring Bitmap es muy aplicable en muchos escenarios. Por ejemplo, se usa ampliamente en análisis de big data, almacenamiento de datos, procesamiento de flujo de datos en tiempo real, motores de búsqueda y otros escenarios.

Roaring Bitmap funciona de la siguiente manera:

  1. Divida el mapa de bits en varios contenedores : Roaring Bitmap divide el mapa de bits de 64 bits en varios contenedores de 16 bits. Cada contenedor contiene 65536 bits.

  2. Se adoptan diferentes estructuras de datos según el número de 1 en el contenedor : Roaring Bitmap seleccionará diferentes estructuras de datos para el almacenamiento según el número de 1 en el contenedor. Por ejemplo, cuando el 1 en un contenedor es muy escaso, Roaring Bitmap elegirá una estructura de datos llamada "Array" para el almacenamiento; cuando el 1 en un contenedor es muy denso, Roaring Bitmap elegirá una estructura de datos llamada "Bitmap". estructura para el almacenamiento; cuando el 1 en un contenedor está muy concentrado, Roaring Bitmap elegirá una estructura de datos llamada "Ejecutar" para el almacenamiento.

  3. Cambiar dinámicamente las estructuras de datos : Roaring Bitmap cambiará dinámicamente las estructuras de datos a medida que cambien los datos. Por ejemplo, Roaring Bitmap cambia de "Array" a "Bitmap" cuando los 1 en un contenedor se vuelven más densos.

En resumen, Roaring Bitmap es una implementación de mapa de bits muy eficiente y flexible, adecuada para escenarios con grandes cantidades de datos. No solo puede almacenar datos de manera eficiente, sino que también proporciona una serie de operaciones rápidas para satisfacer diversas necesidades.

22.3.1 Quién lanzó Roaring Bitmap

Roaring Bitmap fue lanzado conjuntamente por Daniel Lemire, Owen Kaser, Nathan Kurz y otros. Uno de los principales iniciadores y contribuyentes de esta tecnología es Daniel Lemire, profesor de la Universidad de Montreal en Canadá y desarrollador activo de software de código abierto.

22.3.2 Detección de la distribución de 1 en el contenedor (disperso, denso, concentrado):

  • Disperso: se dice que un contenedor es disperso si el número de 1 que contiene es relativamente pequeño. Roaring Bitmap almacenará estas 1 posiciones directamente en una matriz, esta estructura de datos se llama "Array".
  • Denso: un contenedor se considera denso si el número de 1 en él es relativamente alto, acercándose o superando un cierto umbral (por ejemplo, la mitad del tamaño del contenedor). Roaring Bitmap representará este contenedor como un mapa de bits, y esta estructura de datos se denomina "mapa de bits".
  • Concentración: Se dice que un contenedor está concentrado si el número de 1 en él es continuo o está concentrado dentro de un cierto rango. Roaring Bitmap representará estos 1 consecutivos como una posición inicial y una longitud. Esta estructura de datos se denomina "Ejecutar".

22.3.3 "Ejecutar" es una estructura de datos especial utilizada para representar unos consecutivos. Una carrera contiene dos campos: posición de inicio y duración. Por ejemplo, si los bits del 3 al 10 en un contenedor son todos 1, entonces se puede usar Run para representar el rango, la posición inicial es 3 y la longitud es 8. Dame una demostración de la estructura de ejecución

La estructura de datos "Ejecutar" no existe como una clase separada en la biblioteca RoaringBitmap de Java, pero se implementa como parte de RoaringBitmap. Esta estructura de datos es la más adecuada para representar 1 en bits consecutivos. Pero puedo ilustrar con un código Java simple cómo representar y manipular la estructura "Ejecutar".

public class Run {
    
    
    private int start; // 起始位置
    private int length; // 长度

    public Run(int start, int length) {
    
    
        this.start = start;
        this.length = length;
    }

    // 检查一个值是否在这个Run范围内
    public boolean contains(int value) {
    
    
        return value >= start && value < start + length;
    }

    // 获取这个Run的起始位置
    public int getStart() {
    
    
        return start;
    }

    // 获取这个Run的长度
    public int getLength() {
    
    
        return length;
    }

    // 获取这个Run的结束位置
    public int getEnd() {
    
    
        return start + length;
    }

    // 扩展这个Run的长度
    public void extend(int len) {
    
    
        this.length += len;
    }

    @Override
    public String toString() {
    
    
        return "Run [start=" + start + ", length=" + length + "]";
    }

    public static void main(String[] args) {
    
    
        Run run = new Run(3, 8);
        System.out.println("Run: " + run); // Run [start=3, length=8]

        System.out.println("Contains 2? " + run.contains(2)); // false
        System.out.println("Contains 5? " + run.contains(5)); // true

        System.out.println("Start: " + run.getStart()); // 3
        System.out.println("Length: " + run.getLength()); // 8
        System.out.println("End: " + run.getEnd()); // 11

        run.extend(3);
        System.out.println("Extended Run: " + run); // Run [start=3, length=11]
    }
}

En este ejemplo, definimos una Runclase con dos campos: starty length. También definimos algunos métodos para manipular la estructura de datos de ejecución, como comprobar si un valor está dentro del rango de ejecución, obtener la posición de inicio y la duración de la ejecución, etc. Este ejemplo puede brindarle una comprensión básica, pero en la implementación real de Roaring Bitmap, la estructura de datos Run y ​​otras estructuras de datos como Array y Bitmap serán más complejas y optimizadas.

Supongo que te gusta

Origin blog.csdn.net/yxg520s/article/details/132299837
Recomendado
Clasificación