环境

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数据类型的更多相关文章

  1. jedis keys和scan操作

    关于redis的keys命令的性能问题 KEYS pattern 查找所有符合给定模式 pattern 的 key . KEYS * 匹配数据库中所有 key . KEYS h?llo 匹配 hell ...

  2. Jedis运用scan删除正则匹配的key

    jedis运用scan删除正则匹配的key  我们都知道用keys *进行查询key的时候会进行堵塞,导致redis整体不可用,而使用scan命令则不会.   RedisServiceImpl中sca ...

  3. 第三百零六节,Django框架,models.py模块,数据库操作——创建表、数据类型、索引、admin后台,补充Django目录说明以及全局配置文件配置

    Django框架,models.py模块,数据库操作——创建表.数据类型.索引.admin后台,补充Django目录说明以及全局配置文件配置 数据库配置 django默认支持sqlite,mysql, ...

  4. Major compaction时的scan操作

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/u014393917/article/details/24419355 Major compactio ...

  5. 四 Django框架,models.py模块,数据库操作——创建表、数据类型、索引、admin后台,补充Django目录说明以及全局配置文件配置

    Django框架,models.py模块,数据库操作——创建表.数据类型.索引.admin后台,补充Django目录说明以及全局配置文件配置 数据库配置 django默认支持sqlite,mysql, ...

  6. SQLAlchemy02 /SQLAlchemy对数据的增删改查操作、属性常用数据类型详解

    SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 目录 SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 1.用se ...

  7. SQLAlchemy(二):SQLAlchemy对数据的增删改查操作、属性常用数据类型详解

    SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 目录 SQLAlchemy02 /SQLAlchemy对数据的增删改查操作.属性常用数据类型详解 1.用se ...

  8. Java 使用Jedis和RedisTemplate操作Redis缓存(SpringBoot)

    package com.example.redis.controller; import com.example.redis.entity.User; import com.example.redis ...

  9. 存储引擎和表的操作(mysql中的数据类型、完整性约束)

    一.存储引擎 .概念 MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力. 通过选择不同的技术 ...

随机推荐

  1. day5_python之协程函数

    一.yield 1:把函数的执行结果封装好__iter__和__next__,即得到一个迭代器2:与return功能类似,都可以返回值,但不同的是,return只能返回一次值,而yield可以返回多次 ...

  2. 原生sql和 TP sql怎么关联?

    整合后

  3. Jackson-deserialization fails on circular dependencies(JackSon无限递归问题)

    Ok, so I'm trying to test some stuffs with jackson json converter. I'm trying to simulate a graph be ...

  4. mysql 第二课 DML操作

    DML 数据操纵语句:INSERT UPDATE DELETE SELECT 主要用来对数据库的数据进行一些操作; DCL 数据定义语句:CREATE ALTER DROP  主要是用在定义或改变表的 ...

  5. H3C 单路径网络中环路产生过程(1)

  6. linux主编号的动态分配

    一些主设备编号是静态分派给最普通的设备的. 一个这些设备的列表在内核源码树的 Documentation/devices.txt 中. 分配给你的新驱动使用一个已经分配的静态编号的机会 很小, 但是, ...

  7. vue 弹窗时 监听手机返回键关闭弹窗(页面不跳转)

    [注]:  popstate 事件 a.当活动历史记录条目更改时,将触发popstate事件. b.如果被激活的历史记录条目是通过对history.pushState()的调用创建的,或者受到对his ...

  8. HTTP请求各参数详解

    HTTP Request的Header信息 1.HTTP请求方式 如下表: GET 向Web服务器请求一个文件 POST 向Web服务器发送数据让Web服务器进行处理 PUT 向Web服务器发送数据并 ...

  9. 1024程序员节!(JAVA Code)

    点我:传送门  程序员节快乐~ 水水题 A import java.util.*; import java.io.*; public class Main { public static void m ...

  10. hihocoeder1384

    hihocoeder1384 算法竞赛进阶指南上的题目 我们肯定是吧最大值和最小值匹配,次大值和次小值匹配以此类推 首先,类似于区间覆盖的思想,我们对于一个\(L\),找到最大的满足条件的\(R\) ...