La Red de Conocimientos Pedagógicos - Conocimientos matemáticos - Explicación detallada de las preguntas de la entrevista de Dachang: ¿Cómo utilizar Redis para implementar bloqueos distribuidos?

Explicación detallada de las preguntas de la entrevista de Dachang: ¿Cómo utilizar Redis para implementar bloqueos distribuidos?

Hablemos de una pregunta común en las entrevistas:

Una respuesta simple es utilizar el cliente Redission. El esquema de bloqueo en Redis es el esquema detallado del bloqueo distribuido de Redis.

Entonces, ¿por qué el esquema de bloqueo en Redission es perfecto?

Da la casualidad de que tengo una amplia experiencia en el uso de Redis para bloqueos distribuidos. En el trabajo real, también exploré muchas soluciones para usar Redis para bloqueos distribuidos y aprendí innumerables lecciones a través de sangre y lágrimas.

Entonces, antes de hablar sobre por qué los bloqueos de Redission son perfectos, permítanme mostrarles primero los problemas que encontré al usar Redis para bloqueos distribuidos.

Una vez utilicé Redis como un bloqueo distribuido para resolver el problema de que un usuario obtuviera cupones. El requisito comercial es el siguiente: el usuario recibe un cupón y la cantidad de cupones debe reducirse en uno. Si se roba el cupón, el usuario no podrá recuperarlo nuevamente.

Al implementar, primero lea la cantidad de cupones de la base de datos para juzgar. Cuando el cupón es mayor que 0, se permite cobrar el cupón. Luego, la cantidad de cupones se reduce en uno y se vuelve a escribir en la base de datos.

Debido al gran volumen de solicitudes en ese momento, utilizamos tres servidores para la descarga.

Surgirá un problema en este momento:

Si la aplicación A en uno de los servidores obtiene el número de cupones, no actualiza el número en la base de datos a tiempo debido al procesamiento. lógica empresarial relacionada: la cantidad de cupones; mientras la aplicación A procesa la lógica empresarial, la aplicación B en otro servidor actualiza la cantidad de cupones. Luego, cuando la Aplicación A actualice la cantidad de cupones en la base de datos, sobrescribirá la cantidad de cupones actualizados por la Aplicación B.

Al ver esto, algunas personas pueden preguntarse por qué no se usa SQL directamente aquí:

La razón es que sin la coordinación de los candados distribuidos, el número de cupones puede ser directamente negativo. Debido a que el número actual de cupones es 1, si dos usuarios solicitan obtener cupones a través de dos servidores al mismo tiempo, ambos cumplen con todas las condiciones de que el cupón sea mayor que 0 y luego ambos ejecutan esta instrucción SQL, el número de cupones se convierte directamente en -1.

Algunas personas dicen que se puede usar el bloqueo optimista, como usar el siguiente SQL:

De esta manera, bajo cierta probabilidad, es posible que los datos no se actualicen todo el tiempo. lo que resulta en un largo tiempo de reintento.

Entonces, después de una consideración exhaustiva, utilizamos el bloqueo distribuido de Redis para evitar que varios clientes actualicen la cantidad de cupones al mismo tiempo mediante exclusión mutua.

Lo primero que pensamos en ese momento fue usar el comando setnx de Redis, que en realidad es la abreviatura de set si no existe.

Cuando el valor de configuración de la clave es exitoso, se devuelve 1; de lo contrario, se devuelve 0. Por lo tanto, el éxito de configurar setnx aquí se puede expresar como la adquisición del bloqueo. Si falla, significa que ya existe un bloqueo y se puede considerar como un fracaso en la adquisición del bloqueo.

Si deseas liberar el bloqueo, ejecuta el comando task del y elimina la clave.

Al usar esta función, podemos permitir que el sistema ejecute el comando setnx en Redis antes de ejecutar la lógica del cupón. Luego, según el resultado de la ejecución de la instrucción, se juzga si se obtiene el bloqueo. Si se obtiene, continúe ejecutando el negocio y utilice la instrucción del para liberar el bloqueo después de la ejecución. Si no lo consigues, espera un tiempo determinado y vuelve a conseguir el candado.

A primera vista, esto no tiene nada de malo y el uso de la instrucción setnx logra el efecto de exclusión mutua esperado.

Sin embargo, esto se basa en el funcionamiento normal de todos los entornos operativos. Una vez que el entorno operativo es anormal, surgen problemas.

Piénselo, ¿qué pasará si la aplicación que mantiene el bloqueo falla repentinamente o el servidor donde está ubicado se cae?

Esto puede provocar un punto muerto: la aplicación que mantiene el bloqueo no puede liberarlo y otras aplicaciones no tienen posibilidad de obtenerlo. Esto provocará enormes accidentes en línea y deberíamos mejorar la solución para resolver este problema.

¿Cómo solucionarlo? Podemos ver que la causa principal del punto muerto es que una vez que la aplicación que mantiene el bloqueo tiene un problema, no lo libera. Teniendo en cuenta esta dirección, puede darle a la clave un tiempo de vencimiento en Redis.

En este caso, aunque haya algún problema, la clave se liberará al cabo de un tiempo. ¿Esto resolverá el problema? De hecho, todo el mundo tiene uno.

Sin embargo, debido a que la instrucción setnx en sí no puede establecer un tiempo de espera, generalmente hay dos formas de hacerlo:

1. Después de usar el comando setnx, el comando expire se usa para establecer el tiempo de vencimiento de la clave.

2. Utilice directamente el comando set(key, value, NX, EX, timeout) para configurar el bloqueo y el tiempo de espera al mismo tiempo.

Ambos métodos anteriores son aceptables.

Hay dos formas de liberar el script de bloqueo. Puede llamar directamente al comando Redis del.

Hasta ahora, nuestras cerraduras no solo tienen un efecto de exclusión mutua, sino que tampoco causarán interbloqueos debido a problemas con algunos sistemas que mantienen las cerraduras. ¿Es esto perfecto?

¿Qué pasa si hay una situación en la que la aplicación que mantiene el bloqueo excede el tiempo de espera que configuramos? Se producirán dos situaciones:

La primera situación es normal. Como después de todo hay que trabajar horas extras, es natural que las claves se borre normalmente.

Pero lo más aterrador es el segundo caso, la clave establecida todavía existe. ¿Qué quiere decir esto? Explicación La clave existente fue establecida por otra aplicación.

En este momento, si la aplicación que mantiene el bloqueo se agota y llama al comando del para eliminar el bloqueo, el bloqueo establecido por otros se eliminará por error, lo que provocará directamente problemas en el negocio del sistema.

Entonces, para resolver este problema, necesitamos continuar haciendo modificaciones al script de Redis... lo arruinó, cansado...

Primero, debemos dejar que la aplicación establezca un valor único que solo la aplicación conoce cuando adquiere el bloqueo.

Con este valor único, el sistema puede identificar si el bloqueo fue activado por sí mismo al soltarlo. Si lo configura usted mismo, desbloquéelo, es decir, elimine la clave, si no, no haga nada;

El script es el siguiente:

o

Aquí ARGV[1] es una variable de parámetro que se puede pasar y se puede pasar un valor único. en. Por ejemplo, un valor UUID que solo usted conoce o una identificación única que solo usted posee mediante un algoritmo de bola de nieve.

El script para liberar el bloqueo se cambia a:

Como puede ver, desde un punto de vista empresarial, nuestro bloqueo distribuido puede satisfacer las necesidades reales del negocio pase lo que pase. Puede ser mutuamente excluyente, sin punto muerto y sin eliminar accidentalmente los bloqueos de otras personas. Solo se puede liberar su propio bloqueo.

¡Todo es tan hermoso! ! !

Desafortunadamente, existe otro peligro oculto que no hemos eliminado. El peligro oculto es el propio Redis.

Debes saber que todos los scripts de Lua se utilizan en Redis. Una vez que hay un problema con Redis en sí, nuestros bloqueos distribuidos no se pueden usar y los bloqueos distribuidos no se pueden usar, lo que tendrá un impacto significativo en el funcionamiento normal del negocio, lo cual es inaceptable para nosotros.

Por lo tanto, debemos hacer que Redis tenga alta disponibilidad. En términos generales, el clúster maestro-esclavo se utiliza para resolver el problema de alta disponibilidad de Redis.

Sin embargo, establecer un clúster maestro-esclavo introducirá nuevos problemas. El principal problema es el retraso en la sincronización de datos maestro-esclavo de Redis. Este retraso creará una condición límite: cuando Redis en el servidor maestro está bloqueado, pero los datos de bloqueo aún no se han sincronizado con el servidor esclavo, el servidor maestro se apaga. Posteriormente, el esclavo es ascendido a amo. En este momento, no hay datos de bloqueo establecidos previamente por el host; el bloqueo se perdió... se perdió... se perdió...

En este punto, Redission (cliente Redis de código abierto) finalmente puede ser introducido. Veamos cómo implementa el bloqueo distribuido de Redis.

La idea de implementar bloqueos distribuidos en Redission es muy simple. Ya sea un clúster maestro-esclavo o un clúster de Redis, el script para configurar el bloqueo de Redis para cada Redis en el clúster se ejecutará uno por uno, es decir, cada Redis en el clúster contendrá los datos para configurar el bloqueo.

El siguiente es un ejemplo.

Supongamos que hay cinco máquinas en el clúster de Redis. Según la evaluación, es más apropiado establecer el tiempo de espera de bloqueo en 10 segundos.

En el paso 1, primero calculamos el tiempo de espera total del clúster. El tiempo de espera total para el clúster es de 5 segundos (el tiempo de espera de bloqueo es de 10 segundos/2).

El segundo paso es dividir 5 segundos entre el número de 5 máquinas y el resultado es 1 segundo. Este segundo es el tiempo de espera aceptable para cada conexión de Redis.

El tercer paso es conectar cinco Redis en secuencia, ejecutar el script Lua para configurar el bloqueo y luego emitir un juicio:

Por cierto, en muchas lógicas empresariales, el En realidad, el tiempo de espera de bloqueo no es necesario.

Por ejemplo, las tareas que se agrupan en las primeras horas de la mañana pueden requerir bloqueos distribuidos para garantizar que las tareas no se ejecuten repetidamente. En este momento, no está claro cuánto tiempo llevará la tarea. Si el tiempo de espera del bloqueo distribuido se establece aquí, no tiene mucho sentido. Sin embargo, si no se establece el tiempo de espera, se producirá un punto muerto.

Entonces, la forma general de resolver este problema es que cada cliente bloqueado inicie un hilo en segundo plano y actualice continuamente el tiempo de espera de la clave en Redis ejecutando un script Lua específico, de modo que no caduque antes de que se complete la tarea. La clave se borrará.

El script es el siguiente:

Entre ellos, ARGV[1] es una variable de parámetro que se puede pasar, que representa el valor único del sistema que mantiene el bloqueo, que es decir, solo el cliente que mantiene el bloqueo puede actualizar el valor de tiempo de espera antes de que se pueda actualizar la clave.

En este punto, se completa un bloqueo distribuido completo. El plan de implementación se resume a continuación:

Este bloqueo distribuido cumple con las siguientes cuatro condiciones:

Por supuesto, en el script de Redission, para garantizar que el bloqueo se pueda volver a abrir. -Ingresado, el script Lua Se ha realizado un cierto grado de modificación y ahora el script Lua completo se publica a continuación.

Script Lua para adquirir el bloqueo:

Script correspondiente a la actualización del tiempo de espera del bloqueo:

Script correspondiente para liberar el bloqueo:

Entonces Hasta ahora, se ha escrito el plan detallado para usar Redis como bloqueo distribuido.

No solo escribí la experiencia llena de obstáculos paso a paso, sino que también enumeré los detalles y las soluciones a varios problemas. Espero que todos puedan ganar algo al leerlo.

Finalmente, un recordatorio de que el uso del clúster de Redis para bloqueos distribuidos es controvertido y es necesario tomar mejores decisiones y concesiones en función de la situación real.

Blogs originales.com/siyuanwai/p/16011836.html