Node-redis: Manera eficiente de borrar claves por patrón

Creado en 5 mar. 2018  ·  12Comentarios  ·  Fuente: NodeRedis/node-redis

Estoy trabajando en un gran proyecto y, en algunos lugares, tengo que eliminar claves según un patrón, por ejemplo, quiero eliminar todas las claves que comienzan con filterProducts

Hice una búsqueda en Google y encontré algunos métodos para hacerlo, como, por ejemplo, el método de este artículo.

Pero no sé cuál es la más eficiente para un gran proyecto, ¿podrían ayudarme?

  • Versión : 2.8.0
  • Plataforma : Linux Mint 18.2 (construido en Ubuntu 16.04)

Comentario más útil

Para cualquiera que busque el resultado final al que hemos llegado hasta ahora y crea que es la forma más eficiente es la siguiente:

//key example "prefix*"
function getKeysByPattern(key) {
    return new Promise((resolve, reject) => {
        var stream = redis.scanStream({
            // only returns keys following the pattern of "key"
            match: key,
            // returns approximately 100 elements per call
            count: 100
        });

        var keys = [];
        stream.on('data', function (resultKeys) {
            // `resultKeys` is an array of strings representing key names
            for (var i = 0; i < resultKeys.length; i++) {
                keys.push(resultKeys[i]);
            }
        });
        stream.on('end', function () {
            resolve(keys)
        });
    })
}

//key example "prefix*"
function deleteKeysByPattern(key) {
    var stream = redis.scanStream({
        // only returns keys following the pattern of "key"
        match: key,
        // returns approximately 100 elements per call
        count: 100
    });

    var keys = [];
    stream.on('data', function (resultKeys) {
        // `resultKeys` is an array of strings representing key names
        for (var i = 0; i < resultKeys.length; i++) {
            keys.push(resultKeys[i]);
        }
    });
    stream.on('end', function () {
        redis.unlink(keys)
    });
}

//key example "prefix*"
function batchDeletionKeysByPattern(key) {
    var stream = redis.scanStream({
        // only returns keys following the pattern of "key"
        match: key,
        // returns approximately 100 elements per call
        count: 100
    });

    stream.on('data', function (resultKeys) {
        if (resultKeys.length) {
            redis.unlink(resultKeys);
        }
    });
}

El código anterior es para ioredis .
Por favor, si usted es alguien que conoce una mejor manera de hacerlo, no dude en compartirlo.

Todos 12 comentarios

El método al que se vinculó es una forma bastante terrible de hacerlo. KEYS es válido para la producción y DEL también es bastante ineficiente. Comprobaría el uso de SCAN y UNLINK . De esta manera, puede eliminar un puñado de claves a la vez sin inmovilizar Redis.

Dicho todo esto, ese tipo de script depende de usted y está más allá del alcance de la biblioteca cliente. Voy a cerrar el tema.

@ MohammedAl-Mahdawi Tengo un caso de uso similar en uno de mis proyectos y uso la implementación scanStream ofrecida por ioredis. Esta es básicamente una forma más fácil de usar el comando scan : https://github.com/luin/ioredis#streamify -scanning

Si usa SCAN y UNLINK como @stockholmux ya mencionado, debería poder eliminar cualquier número de claves sin bloquear Redis.

@dirkbonhomme Muchas gracias por su ayuda, se lo agradezco mucho.

Seguí sus consejos, utilicé ioredis y creé las siguientes funciones, ¿podría decirme que para grandes proyectos es la siguiente manera más eficiente de hacerlo o no?

//key example "prefix*"
function getKeysByPattern(key) {
    return new Promise((resolve, reject) => {
        var stream = redis.scanStream({
            // only returns keys following the pattern of "key"
            match: key,
            // returns approximately 100 elements per call
            count: 100
        });

        var keys = [];
        stream.on('data', function (resultKeys) {
            // `resultKeys` is an array of strings representing key names
            for (var i = 0; i < resultKeys.length; i++) {
                keys.push(resultKeys[i]);
            }
        });
        stream.on('end', function () {
            resolve(keys)
        });
    })
}

//key example "prefix*"
function deleteKeysByPattern(key) {
    var stream = redis.scanStream({
        // only returns keys following the pattern of "key"
        match: key,
        // returns approximately 100 elements per call
        count: 100
    });

    var keys = [];
    stream.on('data', function (resultKeys) {
        // `resultKeys` is an array of strings representing key names
        for (var i = 0; i < resultKeys.length; i++) {
            keys.push(resultKeys[i]);
        }
    });
    stream.on('end', function () {
        redis.unlink(keys)
    });
}

@ MohammedAl-Mahdawi me parece bien. El único problema posible es que cargará todas las claves coincidentes en la memoria a la vez. Si eso es un problema, puede eliminarlos en lotes tan pronto como entren:

stream.on('data', function (resultKeys) {
        if (resultKeys.length) {
              redis.unlink(resultKeys);
        }
    });

@dirkbonhomme y @ MohammedAl-Mahdawi Este método tiene problemas de atomicidad que podrían afectarlo en la producción.

@stockholmux Gracias por su ayuda, entonces, ¿cuál es la solución adecuada a este problema, por favor?

Para cualquiera que busque el resultado final al que hemos llegado hasta ahora y crea que es la forma más eficiente es la siguiente:

//key example "prefix*"
function getKeysByPattern(key) {
    return new Promise((resolve, reject) => {
        var stream = redis.scanStream({
            // only returns keys following the pattern of "key"
            match: key,
            // returns approximately 100 elements per call
            count: 100
        });

        var keys = [];
        stream.on('data', function (resultKeys) {
            // `resultKeys` is an array of strings representing key names
            for (var i = 0; i < resultKeys.length; i++) {
                keys.push(resultKeys[i]);
            }
        });
        stream.on('end', function () {
            resolve(keys)
        });
    })
}

//key example "prefix*"
function deleteKeysByPattern(key) {
    var stream = redis.scanStream({
        // only returns keys following the pattern of "key"
        match: key,
        // returns approximately 100 elements per call
        count: 100
    });

    var keys = [];
    stream.on('data', function (resultKeys) {
        // `resultKeys` is an array of strings representing key names
        for (var i = 0; i < resultKeys.length; i++) {
            keys.push(resultKeys[i]);
        }
    });
    stream.on('end', function () {
        redis.unlink(keys)
    });
}

//key example "prefix*"
function batchDeletionKeysByPattern(key) {
    var stream = redis.scanStream({
        // only returns keys following the pattern of "key"
        match: key,
        // returns approximately 100 elements per call
        count: 100
    });

    stream.on('data', function (resultKeys) {
        if (resultKeys.length) {
            redis.unlink(resultKeys);
        }
    });
}

El código anterior es para ioredis .
Por favor, si usted es alguien que conoce una mejor manera de hacerlo, no dude en compartirlo.

@ MohammedAl-Mahdawi,
Recibo este error alguna idea sobre el mismo
base de código:

let redisDel = () => {
    var key="employees*"

    return new Promise((resolve, reject) => {

        var stream = redis.scanStream({
            // only returns keys following the pattern of "key"
            match: key,
            // returns approximately 100 elements per call
            count: 100
        });

        stream.on('data', function (resultKeys) {
            if (resultKeys.length) {
                console.log(resultKeys)
                redis.unlink(resultKeys);
            }
        });
        stream.on('end', function (resultKeys) {
            resolve()
        })

    })
}

screenshot from 2019-03-05 19 28 10

¿Alguien da la solución perfecta aquí?

@ siddhkadam1881 El comando de desvinculación solo está disponible desde Redis 4 https://redis.io/commands/unlink
Probablemente esté ejecutando una versión anterior. Pruebe del lugar.

Quiero entender como se hace el escaneo ?. Creo que en la solución de @ MohammedAl-Mahdawi, si la eliminación ocurre en lotes, ¿existe la posibilidad de que las nuevas claves agregadas en ese momento que no deseamos eliminar, sean escaneadas y eliminadas? ¿Qué tal un guión lua?

Alguien tiene alguna idea de por qué esta solución no funcionaría en un servidor ElastiCache de 1 instancia muy simple, pero ¿funciona localmente? Puedo borrar las claves normalmente, pero si trato de usar la implementación de la transmisión, nunca devuelve ninguna clave en ElastiCache.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

aletorrado picture aletorrado  ·  6Comentarios

jackycchen picture jackycchen  ·  4Comentarios

dotSlashLu picture dotSlashLu  ·  5Comentarios

ghost picture ghost  ·  3Comentarios

Stono picture Stono  ·  6Comentarios