jedis的scan操作要注意cursor数据类型
环境
jedis3.0.0
背景
在使用jedis的"scan"操作获取redis中某些key时,发现总是出现类型转换的异常——"java.lang.ClassCastException: java.lang.String cannot be cast to [B"
其中,redis中存储的key是byte[]类型,用"scan"操作获取的所有key是封装到一个List<T>中,获取结果后直接通过Set.addAll()存到一个HashSet<byte[]>中,就在用forEach遍历该HashSet时抛出了异常。
Debug
Why
经过debug发现这跟调用"scan(cursor,params)"时传的"cursor"的类型有关。
如上两图所示:
- 当cursor为String类型时,调用的是Jedis类中的scan方法;
- 当cursor为byte[]类型时,调用的则是BinaryJedis中的scan方法。
Jedis类是BinaryJedis的子类
What
接下来看下两者的scan方法——
Jedis.scan(final String cursor, final ScanParams params):
@Override
public ScanResult<String> scan(final String cursor, final ScanParams params) {
checkIsInMultiOrPipeline();
client.scan(cursor, params);
List<Object> result = client.getObjectMultiBulkReply();
String newcursor = new String((byte[]) result.get(0));
List<String> results = new ArrayList<String>();
List<byte[]> rawResults = (List<byte[]>) result.get(1);
for (byte[] bs : rawResults) {
results.add(SafeEncoder.encode(bs));
}
return new ScanResult<String>(newcursor, results);
}
BinaryJedis.scan(final byte[] cursor, final ScanParams params):
public ScanResult<byte[]> scan(final byte[] cursor, final ScanParams params) {
checkIsInMultiOrPipeline();
client.scan(cursor, params);
List<Object> result = client.getObjectMultiBulkReply();
byte[] newcursor = (byte[]) result.get(0);
List<byte[]> rawResults = (List<byte[]>) result.get(1);
return new ScanResult<byte[]>(newcursor, rawResults);
}
可以发现,两者都是通过调用BinaryClient类的scan方法来获取数据,这些数据是一样的,只是两者在封装返回结果时的操作不同而已。BinaryJedis把byte[]类型的原始数据原封不动地返回,而Jedis则是用SafeEncode把原始数据encode成String类型返回。
How
一开始没意识到scan("0",params)
和scan("0".getBytes(),params)
返回结果不同,直接用了前者,结果前者返回的是List<String>类型的数据,直接用addAll(ScanResult.getResult())方法放到HashSet<byte[]>中,而编译器这时是没有提示的,所以自然而然,在遍历HashSet时就抛出了异常。
注:ScanResult提供两个方法获取游标,分别是getCursor()
和getCursorAsBytes()
,前者是String类型,后者是byte[]类型,使用时需要注意。
拓展
此时又产生了新的疑惑,会不会在ScanParams中也存在类似的问题?于是进ScanParams类查看
发现这里对match方法进行了重载(Overload),传入byte[]和String类型的参数结果是一样的。所以ScanParams的pattern可以用String或者byte[]。
启发
在开发中,一定一定一定要注意变量类型,尤其是集合中的泛型。有必要的话,在类型转换前或者在集合类的操作中对变量进行类型检查。
jedis的scan操作要注意cursor数据类型的更多相关文章
- jedis keys和scan操作
关于redis的keys命令的性能问题 KEYS pattern 查找所有符合给定模式 pattern 的 key . KEYS * 匹配数据库中所有 key . KEYS h?llo 匹配 hell ...
- Jedis运用scan删除正则匹配的key
jedis运用scan删除正则匹配的key 我们都知道用keys *进行查询key的时候会进行堵塞,导致redis整体不可用,而使用scan命令则不会. RedisServiceImpl中sca ...
- 第三百零六节,Django框架,models.py模块,数据库操作——创建表、数据类型、索引、admin后台,补充Django目录说明以及全局配置文件配置
Django框架,models.py模块,数据库操作——创建表.数据类型.索引.admin后台,补充Django目录说明以及全局配置文件配置 数据库配置 django默认支持sqlite,mysql, ...
- Major compaction时的scan操作
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u014393917/article/details/24419355 Major compactio ...
- 四 Django框架,models.py模块,数据库操作——创建表、数据类型、索引、admin后台,补充Django目录说明以及全局配置文件配置
Django框架,models.py模块,数据库操作——创建表.数据类型.索引.admin后台,补充Django目录说明以及全局配置文件配置 数据库配置 django默认支持sqlite,mysql, ...
- SQLAlchemy02 /SQLAlchemy对数据的增删改查操作、属性常用数据类型详解
SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 目录 SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 1.用se ...
- SQLAlchemy(二):SQLAlchemy对数据的增删改查操作、属性常用数据类型详解
SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 目录 SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 1.用se ...
- Java 使用Jedis和RedisTemplate操作Redis缓存(SpringBoot)
package com.example.redis.controller; import com.example.redis.entity.User; import com.example.redis ...
- 存储引擎和表的操作(mysql中的数据类型、完整性约束)
一.存储引擎 .概念 MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力. 通过选择不同的技术 ...
随机推荐
- hdu 3805 Triangle Conjecture
Problem - 3805 题意是给出边的长度的,求出边长相等的三角形,输出任意一种答案.边长是1~n的数,每个只能用一次. 其实比较容易可以看出,无论我们怎么操作,只要保持边长总和都是相邻整数就是 ...
- 在Linux/Unix上运行SuperSocket
SuperSocket通过(Mono 2.10或更新版本)来实现跨平台的特性 由于Unix/Linux不同于Windows上的文件路径格式,SuperSocket提供了专用于Unix/Linux系统上 ...
- Math.abs( x )
Math.abs( x ) 下面是参数的详细信息: x : 一个数字 返回值: 返回一个数字的绝对值 <html> <head> <title>JavaScript ...
- gSOAP calc服务端与客户端示例
1. Web服务定义描述头文件 typedef double xsd__double; int ns__add(xsd__double a, xsd__double b, xsd__double &a ...
- java什么是方法的重载(Overload)
概念: 在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型或参数顺序不同即可. 存在的原因: 屏蔽了一个对象的同一类方法由于参数不同所造成的差异. 特点: 与返回值 ...
- 【codeforces 766C】Mahmoud and a Message
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- 2018-8-10-用-sim-卡加密保护资金
title author date CreateTime categories 用 sim 卡加密保护资金 lindexi 2018-08-10 19:16:52 +0800 2018-2-13 17 ...
- [Ramda] Handle Errors in Ramda Pipelines with tryCatch
Handling your logic with composable functions makes your code declarative, leading to code that's ea ...
- 【js】 vue 2.5.1 源码学习(六) initProxy initLifeCycle 渲染函数的作用域代理
大体思路 (五) 1. initProxy 渲染函数的作用域代理 ==> es6 如果支持proxy (hasProxy) 就用proxy 不支持就用 defineProperty() prox ...
- C# Thread.Join();Thread.Abort();
Join() 等待当前线程运行完成后,才继续执行主线程后续代码: Abort() 结束当前线程,继续执行主线程后续代码: Thread.Join(); static void Main(string[ ...