As instâncias do Tair (Redis OSS-compatible) suportam comandos Lua. Use scripts Lua para lidar eficientemente com operações de comparação e definição (CAS), melhorar o desempenho da instância e implementar padrões difíceis ou ineficientes de alcançar por outros meios. Este tópico descreve a sintaxe básica e as diretrizes de uso para scripts Lua.
Sintaxe básica
Otimização de desempenho
Otimizar sobrecarga de memória e rede
Armazenar em cache muitos scripts funcionalmente repetitivos consome muita memória e pode até desencadear um erro de falta de memória (OOM). Veja a seguir um exemplo de uso incorreto:
EVAL "return redis.call('set', 'k1', 'v1')" 0
EVAL "return redis.call('set', 'k2', 'v2')" 0
Solução:
-
Para reduzir o desperdício de memória, evite codificar argumentos como constantes dentro do script Lua.
# Achieves the same functionality as the incorrect example but only caches the script once. EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 k1 v1 EVAL "return redis.call('set', KEYS[1], ARGV[1])" 1 k2 v2 -
Para maior eficiência, recomendamos a seguinte abordagem para reduzir tanto o uso de memória quanto a sobrecarga de rede.
SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])" # After execution, Redis returns "55b22c0d0cedf3866879ce7c854970626dcef0c3". EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1 EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2
Limpar memória de scripts Lua
O cache de scripts Lua consome memória da instância, contribuindo para a métrica used_memory. Se o uso de memória da instância se aproximar ou exceder maxmemory, isso pode desencadear um erro de falta de memória (OOM). Exemplo de erro:
-OOM command not allowed when used memory > 'maxmemory'.
Solução:
Execute o comando SCRIPT FLUSH a partir de um cliente para limpar o cache de scripts Lua. O método recomendado depende da versão da sua instância:
-
Limpeza assíncrona (Recomendado): open source Redis 7.0 ou posterior e Tair (Enterprise Edition) 6.0 ou posterior suportam o comando
SCRIPT FLUSH ASYNC. Este comando limpa o cache de scripts Lua em segundo plano sem bloquear a instância, minimizando o impacto nos seus serviços.SCRIPT FLUSH ASYNC -
Limpeza síncrona: Para versões anteriores, o comando
SCRIPT FLUSHopera de forma síncrona. Se a instância tiver muitos scripts Lua em cache, este comando pode bloqueá-la por um longo período e torná-la indisponível. Use este comando com cautela, preferencialmente fora dos horários de pico.SCRIPT FLUSH
Clique em Purge Data no console limpa apenas os dados e não remove o cache de scripts Lua.
Além disso, evite escrever scripts Lua excessivamente grandes para prevenir alto consumo de memória. Não realize gravações de dados em massa dentro de um script Lua, pois isso pode causar um aumento abrupto no uso de memória e levar a um erro OOM. Se sua lógica de negócio permitir, recomendamos ativar a evicção de dados (ativada por padrão com a política volatile-lru) para economizar espaço de memória. No entanto, a instância não realiza evicção do cache de scripts Lua, independentemente de a evicção de dados estar ativada.
Tratamento de erros
Erro NOSCRIPT
Ao usar o comando EVALSHA, se o script correspondente ao valor sha1 não estiver em cache na instância, ela retornará um erro NOSCRIPT. Exemplo:
(error) NOSCRIPT No matching script. Please use EVAL.
Solução:
Use o comando EVAL ou SCRIPT LOAD para armazenar o script em cache na instância e tente novamente. Contudo, a instância não garante a persistência ou replicação de scripts Lua. Em alguns cenários, como migração de instância ou alteração de configuração, o cache de scripts Lua pode ser limpo. Portanto, sua aplicação cliente deve ser capaz de lidar com esse erro. Para mais informações, consulte Problemas de persistência e replicação.
O exemplo Python a seguir mostra como lidar com um erro NOSCRIPT. Este exemplo usa um script Lua para realizar uma operação de prefixação de string.
Você também pode usar a biblioteca redis-py para Python para lidar com esse tipo de erro. A biblioteca fornece uma classe Script que encapsula a lógica de baixo nível para scripts Lua do Redis, como a captura de erros NOSCRIPT.
import redis
import hashlib
# This function takes a Lua script as a string and returns its SHA1 digest.
def calcSha1(strin):
sha1_obj = hashlib.sha1()
sha1_obj.update(strin.encode('utf-8'))
sha1_val = sha1_obj.hexdigest()
return sha1_val
class MyRedis(redis.Redis):
def __init__(self, host="localhost", port=6379, password=None, decode_responses=False):
redis.Redis.__init__(self, host=host, port=port, password=password, decode_responses=decode_responses)
def prepend_inLua(self, key, value):
script_content = """\
local suffix = redis.call("get", KEYS[1])
local prefix = ARGV[1]
local new_value = prefix..suffix
return redis.call("set", KEYS[1], new_value)
"""
script_sha1 = calcSha1(script_content)
if self.script_exists(script_sha1)[0]: # Check if the script is cached in Redis.
return self.evalsha(script_sha1, 1, key, value) # If cached, run the script with EVALSHA.
else:
return self.eval(script_content, 1, key, value) # Otherwise, run the script with EVAL, which also caches it. Alternatively, you could use SCRIPT LOAD and then EVALSHA.
r = MyRedis(host="r-******.redis.rds.aliyuncs.com", password="***:***", port=6379, decode_responses=True)
print(r.prepend_inLua("k", "v"))
print(r.get("k"))
Erros de tempo limite de script Lua
-
Como os scripts Lua são executados atomicamente, um script lento pode bloquear a instância. Se um script for executado por mais de 5 segundos, a instância retornará um erro
BUSYpara todos os outros comandos até que o script termine a execução.BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.Solução:
Use o comando
SCRIPT KILLpara encerrar o script Lua ou aguarde sua conclusão.NotaO comando
SCRIPT KILLnão surte efeito durante os primeiros 5 segundos de execução de um script lento, pois a instância está bloqueada.Ao escrever scripts Lua, estime o tempo de execução e verifique problemas como loops infinitos. Isso ajuda a evitar que scripts de longa duração bloqueiem a instância e tornem o serviço indisponível. Se necessário, divida scripts longos em menores.
-
Se o script Lua atual já tiver executado um comando de escrita, o comando
SCRIPT KILLnão funcionará. Exemplo de erro:(error) UNKILLABLE Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in a hard way using the SHUTDOWN NOSAVE command.Solução:
No console, acesse a página Instances e clique em restart para a instância correspondente.
Problemas de persistência e replicação
A instância armazena scripts Lua executados em cache indefinidamente, a menos que seja reiniciada ou que o comando SCRIPT FLUSH seja chamado. No entanto, em certas situações, como migração de instância, alteração de configuração, atualização de versão ou failover de HA, a instância não garante a persistência de scripts Lua nem sua replicação para outros nós.
Solução:
Como a instância não garante a persistência ou replicação de scripts, armazene todos os scripts Lua localmente. Quando necessário, use o comando EVAL ou SCRIPT LOAD para recolocar os scripts em cache na instância. Isso evita erros NOSCRIPT que podem ocorrer se o cache de scripts for limpo durante operações como reinicialização de instância ou failover de HA.
Restrições especiais na arquitetura cluster
Restrições da arquitetura cluster
Para garantir atomicidade, um script Lua não pode ser dividido e deve ser executado em um único shard na arquitetura cluster. A instância geralmente usa uma chave para determinar para qual shard rotear o comando. Portanto, especifique pelo menos uma chave ao executar um script Lua em um cluster. Se um script acessar várias chaves, todas devem pertencer ao mesmo slot; caso contrário, o script falhará ou retornará resultados incorretos. Comandos sem chaves, como
KEYS,SCANeFLUSHDB, podem ser executados, mas retornarão dados apenas de um único shard. Essas limitações são inerentes à arquitetura Redis Cluster.Executar o comando
SCRIPT LOADem um único nó não garante que o script esteja armazenado em outros nós.
Códigos de erro no modo proxy
O proxy realiza verificações de sintaxe para identificar proativamente casos em que chaves abrangem vários slots. Isso ajuda a detectar problemas precocemente, facilitando a solução de problemas. O método de verificação do proxy difere do da máquina virtual Lua, resultando em restrições adicionais ao executar comandos Lua no modo proxy. Por exemplo, o comando UNPACK não é suportado, e EVAL, EVALSHA e comandos da série SCRIPT não podem ser usados dentro de transações MULTI/EXEC.
Desative algumas verificações de sintaxe Lua do proxy desligando o parâmetro script_check_enable.
Adicionalmente, para uma instância de divisão de leitura/escrita, se o parâmetro readonly_lua_route_ronode_enable estiver habilitado, o proxy verifica se um script Lua contém apenas comandos de leitura para determinar se pode ser encaminhado para um nó somente leitura. Essa lógica de verificação impõe limitações à sintaxe Lua.
Desativar o parâmetro script_check_enable tem os seguintes efeitos:
Para instâncias de open source Redis 5.0 (versões secundárias anteriores a 5.0.8) ou Redis 4.0 e anteriores, não recomendamos desativar este parâmetro. Fazer isso pode fazer com que um script retorne uma mensagem de sucesso mesmo quando executado incorretamente.
Para outras versões, desativar este parâmetro faz com que o proxy pare de verificar a sintaxe Lua, mas os nós de dados ainda realizarão suas verificações normais de sintaxe.
A seguir estão os códigos de erro específicos e suas causas.
Limitações da arquitetura Redis Cluster
-
Código de erro:
-ERR for redis cluster, eval/evalsha number of keys can't be negative or zero\r\nDescrição: Especifique pelo menos uma chave ao executar um script Lua. O proxy usa a chave para determinar para qual shard encaminhar o script.
# Correct example EVAL "return redis.call('get', KEYS[1])" 1 fooeval # Incorrect example EVAL "return redis.call('get', 'foo')" 0 -
Código de erro:
-ERR 'xxx' command keys must in same slotDescrição: Todas as chaves em um script Lua devem pertencer ao mesmo slot.
# Correct example: EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo {foo}bar # Incorrect example: EVAL "return redis.call('mget', KEYS[1], KEYS[2])" 2 foo foobar
Restrições de sintaxe Lua do proxy
Evite as verificações adicionais de sintaxe Lua do proxy desativando o parâmetro script_check_enable.
-
Código de erro:
-ERR bad lua script for redis cluster, nested redis.call/redis.pcallDescrição: Chamadas aninhadas ao Redis não são suportadas. Use variáveis locais como alternativa.
# Correct example EVAL "local value = redis.call('GET', KEYS[1]); redis.call('SET', KEYS[2], value)" 2 foo bar # Incorrect example EVAL "redis.call('SET', KEYS[1], redis.call('GET', KEYS[2]))" 2 foo bar -
Código de erro:
-ERR bad lua script for redis cluster, first parameter of redis.call/redis.pcall must be a single literal stringDescrição: O comando invocado dentro de redis.call/pcall deve ser uma string literal.
# Correct example eval "redis.call('GET', KEYS[1])" 1 foo # Incorrect example eval "local cmd = 'GET'; redis.call(cmd, KEYS[1])" 1 foo
Problemas de permissão de leitura/escrita
-
Código de erro:
-ERR Write commands are not allowed from read-only scriptsDescrição: Scripts Lua enviados com o comando
EVAL_ROnão podem conter comandos de escrita. -
Código de erro:
-ERR bad write command in no write privilegeDescrição: Scripts Lua enviados de uma conta somente leitura não podem conter comandos de escrita.
Comandos não suportados
-
Código de erro:
-ERR script debug not supportDescrição: Atualmente, o proxy não suporta o comando
SCRIPT DEBUG. -
Código de erro:
-ERR bad lua script for redis cluster, redis.call/pcall unkown redis command xxxDescrição: O script Lua contém um comando que não é suportado pelo proxy. Para mais informações, consulte Restrições de comandos em instâncias cluster e de divisão de leitura/escrita.
Erros de sintaxe Lua
-
Código de erro:
-ERR bad lua script for redis cluster, redis.call/pcall expect '('ou-ERR bad lua script for redis cluster, redis.call/redis.pcall definition is not complete, expect ')'Nota: Trata-se de um erro de sintaxe Lua, pois a função
redis.calldeve ser seguida por um par completo de parênteses:(e). -
Código de erro:
-ERR bad lua script for redis cluster, at least 1 input key is needed for ZUNIONSTORE/ZINTERSTOREDescrição: O parâmetro
numkeysdos comandosZUNIONSTOREeZINTERSTOREdeve ser maior que 0. -
Código de erro:
-ERR bad lua script for redis cluster, ZUNIONSTORE/ZINTERSTORE key count < numkeysDescrição: O número real de chaves para o comando
ZUNIONSTOREouZINTERSTOREé menor que o valornumkeysespecificado. -
Código de erro:
-ERR bad lua script for redis cluster, xread/xreadgroup command syntax errorDescrição: A sintaxe do comando
XREADouXREADGROUPestá incorreta. Verifique o número de parâmetros. -
Código de erro:
-ERR bad lua script for redis cluster, xread/xreadgroup command syntax error, streams must be specifiedDescrição: Os comandos
XREADeXREADGROUPdevem incluir o parâmetrostreams. -
Código de erro:
-ERR bad lua script for redis cluster, sort command syntax errorDescrição: A sintaxe do comando
SORTestá incorreta.
Perguntas frequentes
-
P: Posso executar scripts Lua no Data Management (DMS)?
R: O console do Data Management (DMS) atualmente não suporta comandos relacionados a Lua. Para usar scripts Lua, conecte-se à sua instância usando um cliente ou redis-cli.