用redis的scan命令代替keys命令,以及在spring-data-redis中遇到的问题
摘要
本文主要是介绍使用redis scan命令遇到的一些问题总结,scan命令本身没有什么问题,主要是spring-data-redis的问题。
需求
需要遍历redis中key,找到符合某些pattern的所有keys。第一反应当然是
KEYS "ABC*
可以找到前缀是ABC的所有KEYS,时间复杂度O(N)。可以使用,但是在生产环境中,这么使用肯定是不行的,因为生产环境的key的数量比较多,一次查询会block其他操作。而更重要的是一次性返回这么多的key,数据量比较大,网络传输成本高。所以一般生产环境中去找符合某些条件的KEYS一般使用SCAN 或 Sets。
集合来操作比较好理解,一个个的pop出来,但是相当于在原有的数据结构上多了一个keys的set集合。SCAN的不需要多维护这份列表。
SCAN 命令
SCAN命令的有SCAN,SSCAN,HSCAN,ZSCAN。
SCAN的话就是遍历所有的keys
其他的SCAN命令的话是SCAN选中的集合。
SCAN命令是增量的循环,每次调用只会返回一小部分的元素。所以不会有KEYS命令的坑。
SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历。
scan 0
1) "655"
2) 1) "test1"
2) "test2"
返回值一个array,一个是下次循环的cursorId,一个是元素数组。SCAN命令不能保证每次返回的值都是有序的,另外同一个key有可能返回多次,不做区分,需要应用程序去处理。
另外SCAN命令可以指定COUNT,默认是10。但是这个并不是指定多少,就能返回多少,这只是一个提示,并不能保证一定返回这么多条。
spring-data-redis SCAN命令的坑
抛出NoSuchElementException 错误
RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
Cursor c = redisConnection.scan(scanOptions);
while (c.hasNext()) {
c.next();
} java.util.NoSuchElementException at java.util.Collections$EmptyIterator.next(Collections.java:4189) at org.springframework.data.redis.core.ScanCursor.moveNext(ScanCursor.java:215) at org.springframework.data.redis.core.ScanCursor.next(ScanCursor.java:202)
这个错误发生在spring-data-redis-1.6版本中。已经被修掉了,
https://github.com/spring-projects/spring-data-redis/pull/154
看到最后comments 1.5.x 和1.6.x中都修复了,但是不知道为什么1.6.0没有修复。
看下ScanCursor.java 源码,异常时next()方法抛出来的,产生的原因是没有next的元素了。在前面介绍过,SCAN命令返回两个一个cursorId,一个是值数组。即使你指定了返回多少条(COUNT),也不能保证实际会返回多少条,当然包括返回0条。这种情况不会经常发生,当你redis server中有大量小的集合时,而扫描时又扫不到匹配的keys,就会返回0个结果,但这并不表示扫描结束,扫描结束的唯一判断依据是扫描结果返回的cursor = 0
@Override
public T next() { assertCursorIsOpen(); if (!hasNext()) {
throw new NoSuchElementException("No more elements available for cursor " + cursorId + ".");
} T next = moveNext(delegate);
position++; return next;
}
这个错误最好的解决办法是升级spring-data-redis版本。如果没法升级,只能在程序中捕获这个异常,再发一次scan请求。而不是依赖spring-data-redis中的scan请求发送。
多线程环境使用的坑
返回这种错误,
java.lang.ClassCastException: java.lang.Long cannot be cast to java.util.List
at redis.clients.jedis.Connection.getRawObjectMultiBulkReply(Connection.java:230)
at redis.clients.jedis.Connection.getObjectMultiBulkReply(Connection.java:236)
或者unknown reply错误。
这个的原因是在一次full 扫描期间,发送一次scan请求,返回游标结果,connection释放掉了,再发送scan请求时,又拿到一个新的连接。这个在单线程环境下,没有问题,但是在多线程环境下,一般来说没有问题,因为scan 命令server没有状态,只有一个cursorId。一个线程scan一次完了,释放掉连接,再发送时,拿到一个新的连接,没有问题,但是如果拿到其他线程的连接就会出现上述问题。
这个问题在spring-data-redis 1.8 RC1 版本修复。就是每个scan操作的cursor维护一个connection。
如果低版本需要修复的话,就是连接不要交给spring-data-redis管理了,获取一个连接,自己维护。
用redis的scan命令代替keys命令,以及在spring-data-redis中遇到的问题的更多相关文章
- Spring Data Redis学习
本文是从为知笔记上复制过来的,懒得调整格式了,为知笔记版本是带格式的,内容也比这里全.点这里 为知笔记版本 Spring Data Redis 学习 Version 1.8.4.Release 前言 ...
- Redis 千万不要乱用KEYS命令,不然会挨打的
Redis现如今使用的场景越来越多?如何批量删除key呢? 有人说用KEYS命令,刚开始学Redis的时候就是用这个命令列出库中键. KEYS命令要谨慎使用. 为何?客观别急,我们先一步步来看. KE ...
- Spring Data Redis 详解及实战一文搞定
SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问Redis的功能.它提供了与商店互动的低级别和高级别抽象,使用户免受 ...
- Redis与Spring Data Redis
1.Redis概述 1.1介绍 官网:https://redis.io/ Redis是一个开源的使用ANSIC语言编写.支持网络.可基于内存 亦可持久化的日志型.Key-Value型的高性能数据库. ...
- spring mvc Spring Data Redis RedisTemplate [转]
http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...
- Spring Data Redis简介以及项目Demo,RedisTemplate和 Serializer详解
一.概念简介: Redis: Redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写,详细的信息在Redis官网上面有,因为我自己通过google等各种渠道去学习Redis, ...
- Spring Data Redis—Pub/Sub(附Web项目源码)
一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...
- Spring Data Redis—Pub/Sub(附Web项目源码) (转)
一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...
- Spring Data Redis 让 NoSQL 快如闪电(2)
[编者按]本文作者为 Xinyu Liu,文章的第一部分重点概述了 Redis 方方面面的特性.在第二部分,将介绍详细的用例.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 把 Redis ...
随机推荐
- 11_Eclipse中演示Git版本的创建,历史版本的修改,创建分支,合并历史版本和当前版本
1 执行以下案例: 某研发团队2011年初开发了一款名为Apollo的信息系统,目前已发布v1.0版本.此项目初期已有部分基础代码, 研发团队再此基础代码上经过3个月的努力发布了一个功能相对完备 ...
- Android开源项目——带图标文字的底部导航栏IconTabPageIndicator
接下来的博客计划是,在<Android官方技术文档翻译>之间会发一些Android开源项目的介绍,直接剩下的几篇Android技术文档发完,然后就是Android开源项目和Gradle翻译 ...
- FPGA学习笔记(一)Verilog语法基础
一.变量类型 ①数值 数值表示采用 <二进制位数>'<数值表示的进制><数值>的结构. 其中进制可以为b.o.d.h分别代表二.八.十.十六进制. 例如22'd0代 ...
- 理解WebKit和Chromium: Chromium的多进程资源加载机制
转载请注明原文地址:http://blog.csdn.net/milado_nju ##概述 前面介绍了WebKit中的资源加载机制,其实它只是一个框架性的东西,实际的资源加载依赖于各个WebKit移 ...
- C语言中数组转化为字符串的方法
#include<stdio.h> #include <stdlib.h> #include <string.h> #define NR(x) (sizeof(x) ...
- centos 下安装mysql-5.6.11
这次是在centos6.4下安装mysql,在安装之前,你要先确定你的linux已经安装了这些包: wget, gcc-c++, ncurses-devel ,cmake, make ,perl 如果 ...
- 自定义仪表盘PaneView
1.概述 最近学习自定义View,趁着周末做了一个仪表盘练练手,效果还可以,在此分享一下先上效果图(截图有点不清晰,凑合着看下吧) 项目在我的github上https://github.com/xsf ...
- rails4 new没有生成prototype.js之类的脚本解决办法
早期版本的rails在新生成程序时会在/public/javascript目录中自动放入若干个js脚本.不过在rails4.2.0中无论是否加-j选项,都不会生成这些脚本文件了.解决办法是安装prot ...
- 阅读源码(IV)
往期系列: <由阅读源码想到> <由阅读源码想到 | 下篇> <阅读源码(III)> Eric S.Raymond的写于2014年的<How to learn ...
- Spring的事务 之 9.1 数据库事务概述 ——跟我学spring3
9.1 数据库事务概述 事务首先是一系列操作组成的工作单元,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务. 事务必需满足ACID(原子性.一致性.隔离性和持久性 ...