在NODE中使用Redis SCAN

Mad*_*ush 6 redis node.js ecmascript-6 bluebird

我有一些格式的Redis有很多键,我想获得匹配某些模式的键并对它们进行一些操作.我不使用KEYS方法,因为它不建议在生产中使用.使用SCAN我想知道在代码中编写它的最佳方法是什么.我必须做一些像while循环但使用promises的东西,我现在的解决方案看起来像这样(代码简化了一点):

'use strict'
const Promise = require('bluebird');
const config = require('./config');
const client = require('./clinet');

let iterator = 0;
Promise.coroutine(function* () {
  do {
    iterator = yield clinet.scanAsync(iterator, 'myQuery', 'COUNT', config.scanChunkSize)
      .then(data => {
        let nextIterator = data[0];
        let values = data[1];
        //do some magic with values
        return nextIterator;
      })
  } while (iterator !== '0');
})();
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来做到我错过了?

小智 18

您可以使用递归来保持调用扫描直到完成.

function scanAsync(cursor, pattern, returnSet){

    return redisClient.scanAsync(cursor, "MATCH", pattern, "COUNT", "100").then(
        function (reply) {

            cursor = reply[0];
            var keys = reply[1];
            keys.forEach(function(key,i){
                returnSet.add(key);
            });

            if( cursor === '0' ){
                return Array.from(returnSet);
            }else{
                return scanAsync(cursor, pattern, returnSet)
            }

    });
}
Run Code Online (Sandbox Code Playgroud)

传入Set()以确保密钥不重复

myResults = new Set();

scanAsync('0', "NOC-*[^listen]*", myResults).map( 
    function( myResults ){ console.log( myResults); }
);
Run Code Online (Sandbox Code Playgroud)


J.W*_*lfe 18

我意识到这是一个非常古老的问题,但我发现所有其他答案都非常不令人满意。这是使用 async await 以相对干净的方式进行扫描的另一种尝试(不使用另一个外部依赖项)。您可以轻松修改它以连续删除每组找到的键(如果有很多,您可能希望像这样分批处理它们)。将它们推入一个数组只是展示了在这个阶段你可以用它们做的一件非常基本的事情。

const redis = require('redis');
const { promisify } = require('util');

const client = redis.createClient({...opts});
const scan = promisify(client.scan).bind(client);

const scanAll = async (pattern) => {
  const found = [];
  let cursor = '0';

  do {
    const reply = await scan(cursor, 'MATCH', pattern);

    cursor = reply[0];
    found.push(...reply[1]);
  } while (cursor !== '0');

  return found;
}
Run Code Online (Sandbox Code Playgroud)


小智 7

您可以在scan每次迭代和“删除”时尝试使用此代码段(1000)个键。

var cursor = '0';
function scan(pattern,callback){

  redisClient.scan(cursor, 'MATCH',pattern,'COUNT', '1000', function(err, reply){
    if(err){
        throw err;
    }
    cursor = reply[0];
    if(cursor === '0'){
        return callback();
    }else{

        var keys = reply[1];
        keys.forEach(function(key,i){                   
            redisClient.del(key, function(deleteErr, deleteSuccess){
                console.log(key);
            });
        });


        return scan(pattern,callback);
    }
  });
}

scan(strkey,function(){
    console.log('Scan Complete');
});
Run Code Online (Sandbox Code Playgroud)


Zuk*_*uku 6

node-redis模块的不错选择是使用scan iterators。例子:

const redis = require("redis");
const client = redis.createClient();

async function getKeys(pattern="*", count=10) {
    const results = [];
    const iteratorParams = {
        MATCH: pattern,
        COUNT: count
    }
    for await (const key of client.scanIterator(iteratorParams)) {
        results.push(key);
    }
    return results;
}
Run Code Online (Sandbox Code Playgroud)

(当然,for await如果这对您来说足够了,您也可以在循环中动态处理您的密钥,而不将它们存储在额外的数组中)。

如果您不想覆盖扫描参数(MATCH/COUNT),您可以跳过它们并在client.scanIterator()没有参数的情况下执行(然后将使用默认值,MATCH =“*”,COUNT = 10)。