# Redis之 大海捞针 kyes和scan命令
有时候需要从 Redis 实例成千上万的 key 中找出特定前缀的 key 列表来手动处理数据,可能是修改它的值,也可能是删除 key。这里就有一个问题,如何从海量的 key 中找出满足特定前缀的 key 列表来?
Redis 提供了一个简单暴力的指令 keys
用来列出所有满足特定正则字符串规则的 key。
# keys命令
TIP
keys
的速度非常快,但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要匹配找到key,一般来说,生产环境直接禁用掉keys
命令。
时间复杂度
O(N), N 为数据库中 key 的数量
这个指令使用非常简单,提供一个简单的正则字符串即可,但是有很明显的两个缺点。
- 没有 offset、limit 参数,一次性吐出所有满足条件的 key,万一实例中有几百 w 个 key 满足条件,当你看到满屏的字符串刷的没有尽头时,你就知道难受了。
keys
算法是遍历算法,复杂度是 O(n),如果实例中有千万级以上的 key,这个指令就会导致 Redis 服务卡顿,所有读写 Redis 的其它的指令都会被延后甚至会超时报错,因为 Redis 是单线程程序,顺序执行所有指令,其它指令必须等到当前的 keys 指令执行完了才可以继续。
Redis 为了解决这个问题,它在 2.8 版本中加入了大海捞针的指令——scan
# scan命令
- 复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程;
- 提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是一个 hint,返回的结果可多可少;
- 同 keys 一样,它也提供模式匹配功能;
- 服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;
- 返回的结果可能会有重复,需要客户端去重复,这点非常重要;
- 遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;
- 单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零;
# scan命令的基本用法
SCAN
命令是一个基于游标的迭代器(cursor based iterator): SCAN
命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN
命令的游标参数, 以此来延续之前的迭代过程。
当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。
SCAN cursor [MATCH partten] [COUNT count]
1
解释:
- cursor: 游标,一般从 0 开始,使用 SCAN 后会返回一个新的游标
- MATCH: 可选参数,如果需要匹配某个模式下的 key,可以使用这个参数
- COUNT: 可选参数,每次SCAN最多返回多少个键,默认是 10
封装一个类似keys的方法
// in nodejs
const scanKeys = async function (redis, key, count = 10) {
let result = [];
let flag = true;
let cursor = 0;
while (flag) {
const data = await redis.scan(cursor, 'MATCH', key, 'COUNT', count);
result.push(...data[1]);
if (data[0] === '0') {
flag = false;
} else {
cursor = data[0];
}
}
return result;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16