在redis cluster模式下nodejs进行scan操作

2,772 阅读1分钟

redis cluster模式

最近公司要求全部系统的redis均需要改为cluster模式。进行迁移后,我发现,我所开发的某个nodejs项目,不好使了!

经过一番排查,发现是因为代码里使用了scan,而scan是在cluster模式下和哨兵/单机模式是不兼容的!

redis集群模式支撑N个redis master node,自动将数据按key的hash值进行分片,每个master上放一部分数据。在cluster模式下,每次使用scan只能查询单台的redis上的数据(keys *同理):

redis-cli -h [ip] -p [port] --scan cursor MATCH 'user*' COUNT num

因此为了能够使用scan命令查询全部的keys,必须依次连接每一台master节点,分别进行scan。

nodejs环境,我们通常用ioredis来进行redis操作。

初始化redis cluster实例

var Redis = require("ioredis");
var cluster = new Redis.Cluster([
  {
    port: 6380,
    host: "127.0.0.1",
  },
  {
    port: 6381,
    host: "127.0.0.1",
  },
]);

配合web框架可以把cluster挂在web实例app上,以koa为例:

const app = new Koa();
app.redis = cluster

scan操作

遍历所有的master nodes,分别进行scan操作:

  async function scanNodes(str){ //str一般含有*,如user:*
    const {ctx, app} = this
    const nodes = app.redis.nodes('master')
    let listAll = []
    let list = []
    for(let i=0; i<nodes.length; i++){
      list = await this.scanNode(nodes[i], str)
      listAll.push(...list)
    }
    return listAll
  }

  async function scanNode(node, str){
    let vals = []
    let point = 0 //起始游标
    const step = 10 //步长
    let keys = []
    while(vals[0]!= 0)
    {
      point = vals[0] || 0
      vals = await node.scan(point,'count', step, 'match', str)
      vals[1] && vals[1].length && (keys = keys.concat(vals[1]))
    } 
    return keys
  }