1 介绍
Scan 实际上是keys的升级版
- 可以用keys来查询key,在查询的过程中,可以使用通配符。keys虽然用着还算方便,但是没有分页功能。同时因为Redis是单线程,所以key的执行会比较消耗时间,特别是当数据流大的时候,影响整个程序的运行。
- 为了解决keys存在的问题,从Redis2.8中开始,引入了Scan(通过游标分布执行)
- Scan具备keys的功能,但是不会阻塞线程,而且可以控制每次返回的结果数。
2 基本用法
首先准备数据:
public class ScanTest {
public static void main(String[] args) {
Redis redis = new Redis();
redis.exectu(jedis -> {
for (int i = 0; i <500 ; i++) {
jedis.set("k"+i,"v"+i);
}
});
}
}
scan命令一共提供了三个参数:
- 第一个参数是cursor,第二个参数是key,第三个参数是limit。(Redis里面的数据存储和MySQL的存储差不多,一样是一维数组加链表)
- cursor实际上是指一位数组的位置索引,limit则是遍历的一位数组个数(即数组里面的某个位置的那个链表的数据),所以每次返回的数据大小可能不确定
127.0.0.1:6379> scan 0 match k5* count 500
1) "383"
2) 1) "k52"
2) "k51"
3) "k50"
4) "k55"
5) "k53"
6) "k54"
7) "k57"
8) "k56"
9) "k5"
10) "k58"
11) "k59"
"383"是下一次要遍历的cursor,下一次查询用这个即可
3 原理
scan的遍历顺序。
假设目前有三条数据:
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> set db_number v1
OK
127.0.0.1:6379> set key1 key1
OK
127.0.0.1:6379> set myKey myKey
OK
127.0.0.1:6379> scan 0 match * count 1
1) "2"
2) 1) "key1"
2) "db_number"
127.0.0.1:6379> scan 2 match * count 1
1) "1"
2) 1) "myKey"
127.0.0.1:6379> scan 1 match * count 1
1) "0"
2) (empty list or set)
在遍历的过程中,大家发现游标的顺序是 0 2 1,从二进制来看,好像没有规律,但是转为二进制,则是有规律的:
00->10->01->11
这种规律就是高位进1,传统的二进制加法,是从右往左加,这里是从左往右加。
实际上,在Redis中,他的具体计算流程是这样:
- 将要计算的数字反转
- 给反转后的数字加1
- 再反转
00反转00加一01反转10
10反转01加一10(1+1=2即10)反转01
01 10 01 11
那么为什么不是按照0、1、2、3、4…这样的顺序遍历呢?
因为主要考虑到两个问题:
- 字典扩容(不会查询到重复的数据)
- 字典缩容
假如我们将要访问110时,发送了扩容,此时scan就会从0110开始遍历,之前已经被遍历的元素就不会被遍历了。
假如我们将要访问110时,发生缩容,此时scan就会从10开始遍历,这个时候,也会遍历到010,但是010之前的不会再被遍历了。所以,在发生缩容的时候,可能会返回重复的元素。
4 其他用法
scan是一系列的指令,除了遍历所有key之外,也可以遍历某一个类型的key,对应的命令有:
//对应
zscan->zset
hscan->hset
sscan->set