在平时线上Redis维护工作中,有时候需要从Redis实例的成千上万个key中找出特定前缀的key列表来手动处理数据,可能是修改它的值,也可能是删除key。这里就有一个问题,如何从海量的key中找出满足特定前缀的key列表?
Redis提供了一个简单粗暴的指令keys用来列出所有满足特定正则字符串规则的key。
这个指令的使用非常简单,只能提供给一个简单的正则字符串即可,但是有两个很明显的缺点。
(1)没有offset、limit参数,一次性吐出所有满足条件的key,万一实例中有几百万个key满足条件,当你看到满屏的字符串,刷屏没有尽头时,你就直到难受了。
(2)keys算法是遍历算法,复杂度是O(n),如果实例中有千万级以上的key,这个指令就会导致Redis服务卡顿,所有读写Redis的其他指令都会被延后甚至会超时报错,因为Redis是单线程程序,顺序执行所有指令,其他指令必须等到当前的keys指令执行完了才可以继续。
面对这两个显著的缺点该怎么办呢?
Redis为了解决这个问题,在2.8版本中加入了scan指令,scan相比keys具备以下特定。
(1)复杂度虽然也是O(n),但它是通过游标分步进行的,不会阻塞线程。
(2)提供limit参数,可以控制每次返回结果的最大条数,limit只是一个hint,返回的结果可多可少。
(3)同keys一样,它也提供模式匹配功能。
(4)服务器不需要为游标保存状态,游标的唯一状态就是scan返回给客户端的游标整数。
(5)返回的结果可能会有重复,需要客户端去重,这点非常重要。
(6)遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的。
(7)单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零。
scan提供了三个参数,第一个是cursor整数值,第二个是key的正则模式,第三个是遍历的limit hint。第一次遍历时,cursor值为0,然后将返回结果中第一个整数值作为下一次遍历的cursor,一直遍历到返回的cursor值为0时结束。
从上面的过程中可以看见,虽然提供的limit是1000,但是返回的结果却只有10个左右。因为这个limit不是限定返回结果的数量,而是限定服务器单次遍历的字典槽位数量(约等于)。如果将limit设置为10,你会发现返回结果是空的,但是游标值不为零,意味着遍历还没有结束。
scan指令是一系列指令,除了可以遍历所有的key之外,还可以对指定的容器集合进行遍历。比如zscan遍历zset集合元素,hscan遍历hash字典的元素,sscan遍历set集合元素。
网友评论