环境

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. 9 模版语言 jinja2

    from flask import Flask,redirect,render_template,jsonify,send_file,request,Markup,sessionimport json ...

  2. Laravel 修改默认日志文件名称和位置

    修改默认日志位置 我们平常的开发中可能一直把laravel的日志文件放在默认位置不会有什么影响,但如果我们的项目上线时是全量部署,每次部署都是git中最新的代码,那这个时候每次都会清空我们的日志,显示 ...

  3. 【原生JS】评论编辑器 文本操作

    效果图: HTML: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> ...

  4. 2016年NOIP普及组复赛题解

    题目涉及算法: 买铅笔:入门题: 回文日期:枚举: 海港:双指针: 魔法阵:数学推理. 买铅笔 题目链接:https://www.luogu.org/problem/P1909 设至少要买 \(num ...

  5. origin/HEAD -> origin/master 这个分支是干嘛的啊

    ➜ sso git:(master) ✗ git branch -r origin/4.0 origin/HEAD -> origin/master origin/master origin/H ...

  6. Java实现Windows锁屏

    public class LockWindows { public static void main(String [] args) throws Exception { Runtime.getRun ...

  7. codeforces 616D

    题意:给你n个数,找出一个最大的区间,满足:不同的数值个数不超过k; //我开始又看错题了. 以为是找出一个最大区间,里面的数的最大值不超过k; 思路:利用一个窗口滑动,左端点表示当前位置,右端点表示 ...

  8. springboot + rabbitmq发送邮件(保证消息100%投递成功并被消费)

    前言: RabbitMQ相关知识请参考: https://www.jianshu.com/p/cc3d2017e7b3 Linux安装RabbitMQ请参考: https://www.jianshu. ...

  9. Spring security用户URL权限之FilterSecurityInterceptor

    总: 用户通过浏览器发送URL地址,由FilterSecurityInterceptor判断是否具有相应的访问权限. 对于用户请求的方法权限,例如注解@PreAuthorize("hasRo ...

  10. 【u232】围棋游戏

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 为了增强幼儿园小朋友的数数能力,小虎老师给了一个家庭游戏作业.让小虎那一块空的围棋盘,随机在一些方格中 ...