sql >> Base de Datos >  >> NoSQL >> Redis

El script de eliminación de comodines de Redis que usa EVAL, SCAN y DEL devuelve comandos de escritura no permitidos después de comandos no deterministas

ACTUALIZACIÓN: lo siguiente se aplica a las versiones de Redis hasta 3.2. A partir de esa versión, la replicación basada en efectos levanta la prohibición del no determinismo, por lo que todas las apuestas están canceladas (o mejor dicho, activadas).

No puedes (y no debes) mezclar el SCAN familia de comandos con cualquier comando de escritura en un script porque la respuesta del primero depende de las estructuras de datos internas de Redis que, a su vez, son exclusivas del proceso del servidor. Dicho de otra manera, no se garantiza que dos procesos de Redis (por ejemplo, maestro y esclavo) devuelvan las mismas respuestas (por lo que en el contexto de replicación de Redis [que no es una operación, sino una declaración] eso lo rompería).

Redis intenta protegerse contra tales casos bloqueando cualquier comando de escritura (como DEL ) si se ejecuta después de un comando aleatorio (por ejemplo, SCAN pero también TIME , SRANDMEMBER y similares). Estoy seguro de que hay formas de evitarlo, pero ¿le gustaría hacer eso? Recuerde, entrará en un territorio desconocido donde el comportamiento del sistema no está definido.

En su lugar, acepte el hecho de que no debe mezclar lecturas y escrituras aleatorias e intente pensar en un enfoque diferente para resolver su problema, es decir, eliminar un montón de claves de acuerdo con un patrón de forma atómica.

Primero pregúntate si puedes relajar alguno de los requisitos. ¿Tiene que ser atómico? La atomicidad significa que Redis se bloqueará durante la eliminación (independientemente de la implementación final) y que la duración de la operación depende del tamaño del trabajo (es decir, la cantidad de claves que se eliminan y su contenido [eliminar un conjunto grande es más caro que eliminar una cadena corta, por ejemplo]).

Si la atomicidad no es imprescindible, periódicamente/perezosamente SCAN y eliminar en pequeños lotes. Si es obligatorio, comprenda que básicamente está tratando de emular las malas KEYS comando :) Pero puedes hacerlo mejor si tienes conocimiento previo del patrón.

Suponiendo que el patrón se conoce durante el tiempo de ejecución de su aplicación, puede recopilar las claves relevantes (por ejemplo, en un conjunto) y luego usar esa colección para actualizar la eliminación de una manera atómica y segura para la replicación que es más eficiente en comparación con recorrer todo el espacio de claves .

Sin embargo, el problema más "difícil" es si necesita ejecutar coincidencias de patrones ad-hoc mientras garantiza la atomicidad. Si es así, el problema se reduce a obtener una instantánea filtrada por patrón del espacio de claves seguida inmediatamente por una sucesión de eliminaciones (volviendo a enfatizar:mientras la base de datos está bloqueada). En ese caso puedes muy bien usar KEYS dentro de su script Lua y esperar lo mejor... (pero sabiendo muy bien que puede recurrir a SHUTDOWN NOSAVE bastante rápido :P).

La última optimización es indexar el propio espacio de claves. Ambos SCAN y KEYS son básicamente exploraciones de tablas completas, entonces, ¿qué pasaría si tuviéramos que indexar esa tabla? Imagine mantener un índice en los nombres de las claves que se pueden consultar durante una transacción; probablemente pueda usar un conjunto ordenado y rangos lexicográficos (HT @TwBert ) para eliminar la mayoría de las necesidades de coincidencia de patrones. Pero a un costo significativo... no solo estará realizando una doble contabilidad (almacenando los costos del nombre de cada clave en RAM y CPU), sino que se verá obligado a agregar complejidad a su aplicación. ¿Por qué añadir complejidad? Porque para implementar dicho índice, tendría que mantenerlo usted mismo en la capa de la aplicación (y posiblemente en todos sus otros scripts de Lua), ajustando cuidadosamente cada operación de escritura a Redis en una transacción que también actualice el índice.

Suponiendo que haya hecho todo eso (y teniendo en cuenta las trampas obvias como la complejidad adicional potencial de errores, al menos el doble de carga de escritura en Redis, RAM y CPU, restricciones de escalado, etc.), puede darse una palmadita en el hombro y felicítese por usar Redis de una manera para la que no fue diseñado. Si bien las próximas versiones de Redis pueden (o no) incluir mejores soluciones para este desafío (@TwBert:¿quieres hacer una RCP/contrib conjunta y nuevamente hackear Redis un poco? ), antes de probar esto, realmente le recomiendo que reconsidere los requisitos originales y verifique que esté usando Redis correctamente (es decir, diseñando su "esquema" de acuerdo con sus necesidades de acceso a los datos).