Redis Scan的使用方式以及Spring redis的坑
2017-10-17 22:55
501 查看
SpringRedisTemplate针对这个Scan进行了封装,示例使用(针对最新库spring-data-redis-1.8.1.RELEASE):
注意Cursor一定不能关闭,在之前的版本中,这里Cursor需要手动关闭,但是从1.8.0开始,不能手动关闭!否则会报异常。
ScanOptions有两个参数,一个是match,另一个是count,分别对应scan命令的两个参数。
Scan命令源码:
可以看出,Redis的SCAN操作由于其整体的数据设计,无法提供特别准的scan操作,仅仅是一个“can ‘ t guarantee , just do my best”的实现:
提供键空间的遍历操作,支持游标,复杂度O(1), 整体遍历一遍只需要O(N);
提供结果模式匹配;
支持一次返回的数据条数设置,但仅仅是个hints,有时候返回的会多;
弱状态,所有状态只需要客户端需要维护一个游标;
无法提供完整的快照遍历,也就是中间如果有数据修改,可能有些涉及改动的数据遍历不到;
每次返回的数据条数不一定,极度依赖内部实现;
返回的数据可能有重复,应用层必须能够处理重入逻辑;上面的示例代码中,redisTemplate.execute方法是个Set,相当于已经对于返回的key去重
count是每次扫描的key个数,并不是结果集个数。count要根据扫描数据量大小而定,Scan虽然无锁,但是也不能保证在超过百万数据量级别搜索效率;count不能太小,网络交互会变多,count要尽可能的大。在搜索结果集1万以内,建议直接设置为与所搜集大小相同
Set<Object> execute = redisTemplate.execute(new RedisCallback<Set<Object>>() { @Override public Set<Object> doInRedis(RedisConnection connection) throws DataAccessException { Set<Object> binaryKeys = new HashSet<>(); Cursor<byte[]> cursor = connection.scan( new ScanOptions.ScanOptionsBuilder().match("test*").count(1000).build()); while (cursor.hasNext()) { binaryKeys.add(new String(cursor.next())); } return binaryKeys; } });
注意Cursor一定不能关闭,在之前的版本中,这里Cursor需要手动关闭,但是从1.8.0开始,不能手动关闭!否则会报异常。
ScanOptions有两个参数,一个是match,另一个是count,分别对应scan命令的两个参数。
Scan命令源码:
/* Handle the case of a hash table. */ ht = NULL; if (o == NULL) {//键扫描 ht = c->db->dict; } else if (o->type == REDIS_SET && o->encoding == REDIS_ENCODING_HT) { ht = o->ptr; } else if (o->type == REDIS_HASH && o->encoding == REDIS_ENCODING_HT) { ht = o->ptr; count *= 2; /* We return key / value for this type. */ } else if (o->type == REDIS_ZSET && o->encoding == REDIS_ENCODING_SKIPLIST) { zset *zs = o->ptr; ht = zs->dict; count *= 2; /* We return key / value for this type. */ } //由于redis的ziplist, intset等类型数据量挺少,所以可用一次返回的。下面的else if 做这个事情。全部返回一个key 。 if (ht) {//一般的存储,不是intset, ziplist void *privdata[2]; /* We pass two pointers to the callback: the list to which it will * add new elements, and the object containing the dictionary so that * it is possible to fetch more data in a type-dependent way. */ privdata[0] = keys; privdata[1] = o; do { //一个个扫描,从cursor开始,然后调用回调函数将数据设置到keys返回数据集里面。 cursor = dictScan(ht, cursor, scanCallback, privdata); } while (cursor && listLength(keys) < count); } else if (o->type == REDIS_SET) { int pos = 0; int64_t ll; while(intsetGet(o->ptr,pos++,&ll))//将这个set里面的数据全部返回,因为它是压缩的intset,会很小的。 listAddNodeTail(keys,createStringObjectFromLongLong(ll)); cursor = 0; } else if (o->type == REDIS_HASH || o->type == REDIS_ZSET) {//那么一定是ziplist了,字符串表示的数据结构,不会太大。 unsigned char *p = ziplistIndex(o->ptr,0); unsigned char *vstr; unsigned int vlen; long long vll; while(p) {//扫描整个键,然后全部返回这一条。并且返回cursor为0表示没东西了。其实这个就等于没有遍历 ziplistGet(p,&vstr,&vlen,&vll); listAddNodeTail(keys, (vstr != NULL) ? createStringObject((char*)vstr,vlen) : createStringObjectFromLongLong(vll)); p = ziplistNext(o->ptr,p); } cursor = 0; } else { redisPanic("Not handled encoding in SCAN."); }
可以看出,Redis的SCAN操作由于其整体的数据设计,无法提供特别准的scan操作,仅仅是一个“can ‘ t guarantee , just do my best”的实现:
提供键空间的遍历操作,支持游标,复杂度O(1), 整体遍历一遍只需要O(N);
提供结果模式匹配;
支持一次返回的数据条数设置,但仅仅是个hints,有时候返回的会多;
弱状态,所有状态只需要客户端需要维护一个游标;
无法提供完整的快照遍历,也就是中间如果有数据修改,可能有些涉及改动的数据遍历不到;
每次返回的数据条数不一定,极度依赖内部实现;
返回的数据可能有重复,应用层必须能够处理重入逻辑;上面的示例代码中,redisTemplate.execute方法是个Set,相当于已经对于返回的key去重
count是每次扫描的key个数,并不是结果集个数。count要根据扫描数据量大小而定,Scan虽然无锁,但是也不能保证在超过百万数据量级别搜索效率;count不能太小,网络交互会变多,count要尽可能的大。在搜索结果集1万以内,建议直接设置为与所搜集大小相同
相关文章推荐
- Redis Scan的使用方式以及Spring redis的坑
- Redis Scan的使用方式以及Spring redis的坑
- redis在spring和springboot中的使用方式以及遇到的坑
- 深入理解Spring Redis的使用 (七)、Spring Redis 使用 jackson序列化 以及 BaseDao代码
- Redis 一二事 - 在spring中使用jedis 连接调试单机redis以及集群redis
- Spring整合quartz两种方式以及Spring轮询定时使用
- 深入理解Spring Redis的使用 (九)、通过Redis 实现 分布式锁 的 BUG,以及和数据库加锁的性能测试
- spring aop的使用(注解方式以及基于xml配置方式)
- spring整合redis以及使用RedisTemplate的方法
- 关于Spring boot 与 redis 的集成,以及Spring中redis的使用
- spring-data-redis 整合,以及使用kryo序列化代替jdk原生序列化机制
- redis集群的Cluster方式配置以及spring的集成
- (6) Spring 如何装配 集合类 以及 【第二种 依赖注入 方式】使用 构造器 注入
- spring 整合 redis,以及spring的RedisTemplate如何使用
- 深刻剖析spring三种注入方式以及使用注解的原理
- Spring中事务管理支持哪几种方式以及每种方式的具体使用方法
- spring aop的使用(注解方式以及基于xml配置方式)
- 深入理解Spring Redis的使用 (七)、Spring Redis 使用 jackson序列化 以及 BaseDao代码
- Spring学习之使用标签来标记资源(@Component、@Repository、 @Service和@Controller)以及使用方式(包含如何在jsp中使用)
- 深入理解Spring Redis的使用 (九)、通过Redis 实现 分布式锁 的 BUG,以及和数据库加锁的性能测试