Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

O script de exclusão de curinga do Redis usando EVAL, SCAN e DEL retorna comandos de gravação não permitidos após comandos não determinísticos


ATUALIZAÇÃO: o abaixo se aplica às versões do Redis até 3.2. A partir dessa versão, a replicação baseada em efeitos suspende a proibição do não determinismo, de modo que todas as apostas estão desativadas (ou melhor, ativadas).

Você não pode (e não deve) misturar o SCAN família de comandos com qualquer comando de gravação em um script porque a resposta do primeiro depende das estruturas de dados internas do Redis que, por sua vez, são exclusivas do processo do servidor. Em outras palavras, dois processos Redis (por exemplo, mestre e escravo) não têm garantia de retornar as mesmas respostas (portanto, no contexto de replicação Redis [que não é baseado em operação, mas em instrução] isso o quebraria).

O Redis tenta se proteger contra esses casos bloqueando qualquer comando de gravação (como DEL ) se for executado após um comando aleatório (por exemplo, SCAN mas também TIME , SRANDMEMBER e semelhantes). Tenho certeza de que existem maneiras de contornar isso, mas você gostaria de fazer isso? Lembre-se, você estará entrando em território desconhecido onde o comportamento do sistema não está definido.

Em vez disso, aceite o fato de que você não deve misturar leituras e gravações aleatórias e tente pensar em uma abordagem diferente para resolver seu problema, ou seja, excluir um monte de chaves de acordo com um padrão de maneira atômica.

Primeiro pergunte a si mesmo se você pode relaxar qualquer um dos requisitos. Tem que ser atômico? Atomicidade significa que o Redis será bloqueado pela duração da exclusão (independentemente da implementação final) e que a duração da operação depende do tamanho do trabalho (ou seja, número de chaves excluídas e seu conteúdo [excluir um conjunto grande é mais caro do que deletar uma string curta, por exemplo]).

Se a atomicidade não for uma obrigação, periodicamente/preguiçosamente SCAN e excluir em pequenos lotes. Se for obrigatório, entenda que você está basicamente tentando emular as más KEYS command :) Mas você pode fazer melhor se tiver conhecimento prévio do padrão.

Supondo que o padrão seja conhecido durante o tempo de execução do seu aplicativo, você pode coletar as chaves relevantes (por exemplo, em um conjunto) e usar essa coleção para atualizar a exclusão de maneira atômica e segura para replicação que é mais eficiente em comparação a percorrer todo o keyspace .

No entanto, o problema mais "difícil" é se você precisa executar a correspondência de padrões ad-hoc enquanto garante a atomicidade. Nesse caso, o problema se resume a obter um instantâneo filtrado por padrão do keyspace imediatamente seguido por uma sucessão de exclusões (reenfatizando:enquanto o banco de dados estiver bloqueado). Nesse caso, você pode muito bem usar KEYS dentro do seu script Lua e espere pelo melhor... (mas sabendo muito bem que você pode recorrer a SHUTDOWN NOSAVE rapidamente :P).

A última otimização é indexar o próprio keyspace. Ambos SCAN e KEYS são basicamente varreduras de tabela completas, e daí se indexássemos essa tabela? Imagine manter um índice nos nomes das chaves que podem ser consultados durante uma transação - você provavelmente pode usar um Conjunto Ordenado e intervalos lexicográficos (HT @TwBert ) para eliminar a maioria das necessidades de correspondência de padrões. Mas a um custo significativo... além de fazer dupla escrituração (armazenando os custos do nome de cada chave em RAM e CPU), você seria forçado a adicionar complexidade ao seu aplicativo. Por que adicionar complexidade? Porque para implementar tal índice você teria que mantê-lo na camada de aplicação (e possivelmente em todos os seus outros scripts Lua), envolvendo cuidadosamente cada operação de gravação no Redis em uma transação que também atualiza o índice.

Supondo que você tenha feito tudo isso (e levando em conta as armadilhas óbvias, como o potencial adicional de complexidade para bugs, pelo menos dobrou a carga de gravação no Redis, RAM e CPU, restrições de dimensionamento e assim por diante ...) ombro e parabenize-se por usar o Redis de uma maneira para a qual não foi projetado. Embora as próximas versões do Redis possam (ou não) incluir soluções melhores para esse desafio (@TwBert - quer fazer um RCP/contrib conjunto e novamente hackear um pouco o Redis? ), antes de tentar isso, recomendo que você repense os requisitos originais e verifique se está usando o Redis corretamente (ou seja, projetando seu "esquema" de acordo com suas necessidades de acesso a dados).