Orca, el protocolo compatible con Redis en PolarDB for MySQL, admite scripts de Lua para ejecutar varios comandos de forma atómica en una sola interacción con la base de datos. Utilice scripts de Lua para implementar bloqueos distribuidos, limitadores de velocidad y actualizaciones condicionales. Así evitará la latencia y los riesgos de condiciones de carrera que generan los múltiples viajes de ida y vuelta.
Requisitos previos
Antes de empezar, asegúrese de contar con lo siguiente:
Un clúster de PolarDB que ejecute MySQL 8.0.2 con la versión secundaria del motor 8.0.2.2.33 o posterior.
Un endpoint de ORCA configurado con Read/Write establecido en Read/Write (Automatic Read/Write Splitting).
Conceptos clave
Atomicidad: todos los comandos de un script de Lua se ejecutan como una unidad única e indivisible. Ningún otro comando o script puede interrumpir la ejecución, lo cual evita escrituras parciales y estados intermedios.
Caché de scripts: cargue un script una sola vez con SCRIPT LOAD para obtener su suma de comprobación SHA1. Después, invoque ese script de forma repetida con EVALSHA usando únicamente el valor corto de SHA1. Esto reduce el tráfico de red en comparación con enviar el script completo en cada llamada.
Volatilidad de la caché de scripts: la caché de scripts no es persistente. Se borra después de reiniciar el clúster, tras un cambio por alta disponibilidad (HA) o al invocar de manera explícita SCRIPT FLUSH. Diseñe su aplicación para capturar los errores NOSCRIPT y recargar los scripts automáticamente.
Comandos
Todos los comandos de scripting de Lua siguen la misma convención de parámetros: pase los nombres de las claves mediante KEYS[] y los demás argumentos mediante ARGV[]. Los índices de los arreglos comienzan en 1.
Para consultar la especificación completa de Redis, consulte Scripting with Lua y EVAL command reference en redis.io.
|
Comando |
Sintaxis |
Descripción |
|
|
|
|
Ejecuta un script de Lua directamente. |
|
|
|
|
Ejecuta un script de Lua en modo de solo lectura. Devuelve un error si el script contiene comandos de escritura. Úselo en escenarios de división de lectura y escritura. |
|
|
|
|
Ejecuta un script almacenado en la caché mediante su suma de comprobación SHA1. Devuelve un error |
|
|
|
|
Ejecuta un script almacenado en la caché en modo de solo lectura. Tiene el mismo comportamiento que |
|
|
|
|
Carga un script en la caché y devuelve su suma de comprobación SHA1. Use esta suma con |
|
|
|
|
Comprueba si los scripts están en la caché. Devuelve |
|
|
|
|
Finaliza el script que se está ejecutando en ese momento. Orca revierte automáticamente cualquier cambio de datos que haya hecho el script, incluidos los scripts de escritura. |
|
|
|
|
SYNC]` |
Elimina todos los scripts de Lua almacenados en la caché del clúster. |
Comandos no admitidos
A continuación se muestran los comandos que no se pueden invocar dentro de un script de Lua mediante redis.call() ni redis.pcall():
Control de transacciones:
WATCH,UNWATCH,MULTI,EXEC,DISCARDComandos de bloqueo:
BLPOP,BRPOPComandos de scripting de Lua:
EVAL,EVAL_RO,EVALSHA,EVALSHA_RO,SCRIPTComandos Pub/Sub:
SUBSCRIBE,UNSUBSCRIBE,PSUBSCRIBE,PUNSUBSCRIBEGestión de conexiones:
AUTH,HELLO,CLIENT
Si invoca cualquiera de estos comandos, se devolverá un error de inmediato.
Inicio rápido
Los siguientes ejemplos usan la clave de prueba establecida con SET polardb orca.
EVAL
EVAL: ejecuta un script directamente.
EVAL "return redis.call('GET', KEYS[1])" 1 polardb
Salida:
"orca"
SCRIPT LOAD
SCRIPT LOAD: almacena un script en la caché y obtiene su suma de comprobación SHA1.
SCRIPT LOAD "return redis.call('GET', KEYS[1])"
Salida:
"d3c21d0c2b9ca22f82737626a27bcaf5d288f99f"
EVALSHA
EVALSHA: ejecuta el script almacenado en la caché mediante su suma de comprobación SHA1.
EVALSHA d3c21d0c2b9ca22f82737626a27bcaf5d288f99f 1 polardb
Salida:
"orca"
SCRIPT EXISTS
SCRIPT EXISTS: comprueba si los scripts están en la caché (el primer SHA1 existe, el segundo no).
SCRIPT EXISTS d3c21d0c2b9ca22f82737626a27bcaf5d288f99f ffffffffffffffffffffffffffffffffffffffff
Salida:
1) (integer) 1
2) (integer) 0
Escenarios comunes
Implementar un contador atómico
Utilice scripts de Lua para evitar las condiciones de carrera que generan las operaciones INCR simultáneas. El patrón SCRIPT LOAD + EVALSHA evita transmitir el contenido del script en las llamadas posteriores. De este modo, se reduce el tráfico de red y mejora el rendimiento.
1. Escriba el script y guárdelo de forma local:
-- counter.lua
-- Initialize the key to 0 if it does not exist, then increment by 1.
if redis.call('EXISTS', KEYS[1]) == 0 then
redis.call('SET', KEYS[1], 0)
end
return redis.call('INCR', KEYS[1])
2. Cargue el script para obtener su suma de comprobación SHA1:
SCRIPT LOAD "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], 0) end return redis.call('INCR', KEYS[1])"
Salida:
"b547eabbcde73b25330442e4f4e4dc1783b91241"
3. Ejecute el script mediante su suma de comprobación SHA1:
EVALSHA b547eabbcde73b25330442e4f4e4dc1783b91241 1 my_counter
Salida en la primera llamada:
(integer) 1
Salida en la segunda llamada:
(integer) 2
Ejecutar un script de solo lectura
Use EVAL_RO o EVALSHA_RO para garantizar que un script no ejecute operaciones de escritura. Esta opción resulta útil en escenarios de división de lectura y escritura donde los scripts solo deben acceder a las réplicas de lectura.
Antes de usar EVAL_RO, confirme que su script no invoque comandos de escritura como SET, DEL, HSET o LPUSH. Si los incluye, use EVAL en su lugar.
EVAL_RO "return {redis.call('GET', KEYS[1]), redis.call('TTL', KEYS[1])}" 1 polardb
Salida:
1) "orca"
2) (integer) -1
Si el script contiene comandos de escritura, EVAL_RO devuelve (error) ERR Write commands are not allowed from read-only scripts.
Detener un script de ejecución prolongada
Si un script tarda más de lo esperado y bloquea el clúster, finalícelo con SCRIPT KILL. Orca revierte automáticamente todos los cambios que haya hecho el script.
SCRIPT KILL
Salida:
OK
SCRIPT KILL solo finaliza el script que se está ejecutando en ese momento. No es posible seleccionar un script específico mediante su SHA1.
Mejores prácticas
Usar SCRIPT LOAD + EVALSHA en cargas de trabajo de producción
Todos los ejemplos siguientes usan SCRIPT LOAD durante el inicio para almacenar los scripts en la caché y luego emplean EVALSHA en cada llamada posterior. Este patrón evita retransmitir el script completo en cada solicitud.
1. Cargue el script durante la inicialización de la aplicación:
# Cache a script that sets a key's value
SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])"
Salida:
"55b22c0d0cedf3866879ce7c854970626dcef0c3"
Almacene este valor SHA1 en su aplicación.
2. Ejecute el script mediante el SHA1 almacenado en la caché:
# Set k1 = v1
EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1
# Set k2 = v2 using the same cached script
EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2
3. Gestione los errores NOSCRIPT:
Si EVALSHA devuelve un error NOSCRIPT, significa que el script ya no está en la caché. Cambie a EVAL o SCRIPT LOAD para volver a ejecutar el script y almacenarlo de nuevo en la caché. Después, retome el uso de EVALSHA.
Parametrizar scripts con KEYS[] y ARGV[]
Pase todos los datos variables mediante KEYS[] y ARGV[] en lugar de codificar valores de forma fija en el cuerpo del script. Los scripts con valores fijos son cadenas únicas, por lo que cada variante genera una entrada de caché independiente. Esto aumenta el consumo de memoria y anula las ventajas de la caché.
El siguiente ejemplo muestra el antipatrón y el enfoque correcto:
-- Anti-pattern: each hard-coded variant creates a separate cache entry
EVAL "return 'Hello'" 0
EVAL "return 'Scripting!'" 0
-- Correct: one parameterized script reuses the same cache entry for any value
EVAL "return ARGV[1]" 0 Hello
EVAL "return ARGV[1]" 0 Scripting!
Mantener los scripts cortos y rápidos
Los scripts de Lua se ejecutan de forma atómica y bloquean todos los demás comandos durante su ejecución. Los scripts de ejecución prolongada o con lotes grandes aumentan la latencia del clúster para el resto de los clientes.
Evite bucles complejos o recorridos de datos a gran escala dentro de los scripts.
El tiempo de espera de ejecución predeterminado es de 300 segundos. Si un script supera este límite, simplifique la lógica.
Divida las operaciones complejas en varios scripts pequeños en lugar de usar uno solo de gran tamaño.
Borrar la caché de scripts de forma segura
Use SCRIPT FLUSH para eliminar todos los scripts almacenados en la caché. Después de borrarla, cualquier llamada a EVALSHA o EVALSHA_RO devolverá un error NOSCRIPT hasta que se vuelvan a cargar los scripts.
En clústeres con muchos scripts en la caché, SCRIPT FLUSH puede bloquear el clúster durante un periodo prolongado. Ejecute este comando fuera del horario de mayor demanda.
SCRIPT FLUSH
Salida:
OK
Conserve el código fuente de los scripts en su aplicación e implemente una recuperación automática para los errores NOSCRIPT. Cuando ocurra un error, vuelva a cargar el script con SCRIPT LOAD o regístrelo de nuevo con EVAL.
Preguntas frecuentes
¿Cómo soluciono NOSCRIPT No matching script. Please use EVAL.?
El script ya no está en la caché del clúster. Esto ocurre después de reiniciar el clúster, tras un cambio por HA o al invocar SCRIPT FLUSH. En su cliente, capture los errores NOSCRIPT y recurra a EVAL o SCRIPT LOAD para registrar el script nuevamente. Una vez registrado de nuevo, las llamadas a EVALSHA funcionarán con normalidad.
¿Qué debo hacer cuando un script agota el tiempo de espera?
El tiempo de espera predeterminado es de 300 segundos. Un agotamiento de tiempo suele indicar que la lógica del script es demasiado compleja o que procesa demasiados datos. Para recuperarse, ejecute SCRIPT KILL; PolarDB revertirá todos los cambios que haya hecho el script. Para evitar que se repita, optimice el script y evite bucles ineficientes o lecturas de datos a gran escala. También puede dividir la lógica en varios scripts más pequeños.
¿Por qué EVAL_RO devuelve ERR Write commands are not allowed from read-only scripts.?
EVAL_RO y EVALSHA_RO prohíben todas las operaciones de escritura. Revise el script para buscar comandos de escritura como SET, HSET o DEL y elimínelos. Si el script necesita escribir datos, use EVAL o EVALSHA en su lugar.