使用redis的scan指令详解
在生产环境中使用了keys指令之后容易导致出现短时间内的请求堵塞,这种情况在高并发环境中是比较致命的存在,因此需要尽可能地避免这种情况发生。
常用的查询某些key的指令:
scan
jedis使用方式:
public List<String> scanAll(String cursor, String pattern, Integer limit) {try (Jedis jedis = iRedisFactory.getConnection()) {List<String> scanList = new LinkedList<>();ScanParams scanParams = new ScanParams();scanParams.match(pattern);scanParams.count(limit);while (true) {long begin = System.currentTimeMillis();ScanResult<String> scanResult = jedis.scan(cursor, scanParams);if (ScanParams.SCAN_POINTER_START.equals(scanResult.getCursor())) {break;}System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms,查询数目:" + scanResult.getResult().size());scanList.addAll(scanResult.getResult());cursor = scanResult.getCursor();}return scanList;} catch (Exception e) {log.error("jedis scanAll has error, error is ", e);}return null;
}
虽然在使用scan指令的时候复杂度也是o(n)会有部分堵塞,但是由于是多次请求,相当于将之前的keys指令分成了多次小范围的搜索,减少堵塞的时长。
sscan
jedis使用方式
public List<String> sScanAll(String key,String cursor, String pattern, Integer limit) {try(Jedis jedis = iRedisFactory.getConnection()) {List<String> resultList = new LinkedList<>();while (true) {ScanResult<String> scanResult = jedis.sscan(key,cursor);cursor = scanResult.getCursor();resultList.addAll(scanResult.getResult());if (ScanParams.SCAN_POINTER_START.equals(cursor)){break;}}return resultList;}catch (Exception e){log.error("jedis sscanAll has error, error is ", e);}return null;
}
其实sscan指令结合名字就可以猜到,这是一个用于set集合使用的遍历指令,比较推荐在对set集合中进行遍历使用,例如当我们的set集合元素过多的时候,直接使用smembers指令容易造成堵塞,此时使用sscan指令可以减少堵塞的情况发生。
hscan
jedis使用方式
public Map<String,String> hScanAll(String key, String cursor, String pattern, Integer limit) {try(Jedis jedis = iRedisFactory.getConnection()) {ScanParams scanParams = new ScanParams();scanParams.match(pattern);scanParams.count(limit);Map<String,String> resultList = new HashMap<>();while (true) {ScanResult scanResult = jedis.hscan(key,cursor,scanParams);cursor = scanResult.getCursor();if (ScanParams.SCAN_POINTER_START.equals(cursor)){break;}List<Map.Entry<String,String>> result = scanResult.getResult();for (Map.Entry<String, String> entry : result) {resultList.put(entry.getKey(),entry.getValue());}}return resultList;}catch (Exception e){log.error("jedis sscanAll has error, error is ", e);}return null;
}
这是一个用于hashmap集合使用的遍历指令
zscan
jedis使用方式
public List<String> zScanAll(String key, String cursor, String pattern, Integer limit) {try(Jedis jedis = iRedisFactory.getConnection()) {ScanParams scanParams = new ScanParams();scanParams.match(pattern);scanParams.count(limit);List<String> resultList = new LinkedList<>();while (true) {ScanResult scanResult = jedis.zscan(key,cursor,scanParams);cursor = scanResult.getCursor();if (ScanParams.SCAN_POINTER_START.equals(cursor)){break;}resultList.addAll(scanResult.getResult());}return resultList;}catch (Exception e){log.error("jedis sscanAll has error, error is ", e);}return null;
}
这是一个用于有序集合使用的遍历指令
在Centos操作系统上边执行这些指令的案例:
scan指令
redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2) 1) "key:611"2) "key:711"3) "key:118"4) "key:117"5) "key:311"6) "key:112"7) "key:111"8) "key:110"9) "key:113"10) "key:211"11) "key:411"12) "key:115"13) "key:116"14) "key:114"15) "key:119"16) "key:811"17) "key:511"18) "key:11"
sscan指令
> sscan vip-info-set 0 count 10
0
[[1001]]zscan指令
> zscan review:222 0 match * count 10
0
idea
1
idea2
2
idea3
3
idea4
4
hscan指令
> hscan user-map 0 match * count 10
0
1
user1
2
user2
3
user3
SCAN的遍历顺序
关于scan命令的遍历顺序,我们可以具体看一下。
> keys *
user
test-key-1
test-key-3
test-key-4
test-key-2
> scan 0 match * count 1
2
test-key-3
> scan 2 match * count 1
6
user
> scan 6 match * count 1
1
test-key-4
> scan 1 match * count 1
7
test-key-1
> scan 7 match * count 1
0
test-key-2
我们的Redis中有3个key,我们每次只遍历一个一维数组中的元素。如上所示,SCAN命令的遍历顺序是
0->2->6->1->7->0
这个顺序看起来有些奇怪。我们把它转换成二进制就好理解一些了。我们发现每次这个序列是高位加1的。普通二进制的加法,是从右往左相加、进位。而这个序列是从左往右相加、进位的。
000->010->110->001->111->000
那么为什么redis的作者想要这么设计呢?
这个地方涉及到了redis的rehash操作,假设我们原先的索引为:
000->100->010->110->001->101->011->111
那么在进行了一轮扩容之后,索引会变为:
0000->1000->0100->1100->0010->1010->0110->1110->0001->1001->1101->1111
原来挂接在xxx下的所有元素被分配到0xxx和1xxx下。当我们即将遍历010时,dict进行了rehash,这时,scan命令会从0100开始遍历,而000和100(原00下挂接的元素)不会再被重复遍历。
再来看看缩容的情况。假设dict从4位缩容到3位,当即将遍历1110时,dict发生了缩容,这时scan会遍历011。这时011下的元素会被重复遍历,但011之前的元素都不会被重复遍历了。所以,缩容时还是可能会有些重复元素出现的。因此在使用scan指令的时候,时而会出现重复的元素。
使用redis的scan指令详解相关推荐
- redis的info指令详解
在使用 Redis 时,时常会遇到很多问题需要诊断,在诊断之前需要了解 Redis 的运行状态,通过强大的 Info 指令,你可以清晰地知道 Redis 内部一系列运行参数. Info 指令显示的信息 ...
- Redis数据库教程——系统详解学习Redis全过程
Redis数据库教程--系统详解学习Redis全过程 Redis快速入门:Key-Value存储系统简介 Key-Value存储系统: Key-Value Store是当下比较流行的话题,尤其 ...
- redis stream持久化_Beetlex.Redis之Stream功能详解
原标题:Beetlex.Redis之Stream功能详解 有一段时间没有写文章,techempower的测试规则评分竟然发生了变化,只能忘着补充一下占比权重最多的数据更新示例了和深入设计一下组件模块化 ...
- Dockerfile 指令详解1
Dockerfile 指令详解 我们已经介绍了 FROM,RUN,还提及了 COPY, ADD,其实 Dockerfile 功能很强大,它提供了十多个指令.下面我们继续讲解其他的指令. COPY 复制 ...
- Docker技术入门与实战 第二版-学习笔记-3-Dockerfile 指令详解
前面已经讲解了FROM.RUN指令,还提及了COPY.ADD,接下来学习其他的指令 5.Dockerfile 指令详解 1> COPY 复制文件 格式: COPY <源路径> .. ...
- 【Bluetooth LE】Bluez中Bluetoothctl指令详解(连接iPhone为例)
Bluez中Bluetoothctl指令详解 写在前面 准备硬件和软件 ubantu&bluez版本 iPhone&LightBlue Bluetoothctl指令列表 Bluetoo ...
- arm-linux-ld中的参数,arm-linux-ld指令详解
arm-linux-ld指令详解 我们对每个c或者汇编文件进行单独编译,但是不去连接,生成很多.o 的文件,这些.o文件首先是分散的,我们首先要考虑的如何组合起来:其次,这些.o文件存在相互调用的关系 ...
- 九爷带你了解 nginx 日志配置指令详解
nginx日志配置指令详解 日志对于统计排错来说非常有利的. 本文总结了nginx日志相关的配置如 access_log.log_format.open_log_file_cache.log_not_ ...
- movsb movsw movsd 指令详解
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/u013490896/article/d ...
最新文章
- 784.字母大小写全排列
- 我是如何在2年内逆袭成为BAT年薪40W的资深开发工程师的?
- Python使用matplotlib函数subplot可视化多个不同颜色的折线图、使用set_major_formatter函数自定义设置y轴数值标签格式为百分比
- HaoheDI让ETL变得简单
- 一种全新的点击率建模方案
- django ajax 查询,Django分页和Ajax查询
- InternalError: Blas GEMM launch failed : a.shape=(100, 784), b.shape=(784, 10), m=100, n=10...问题解决办法
- 大腾讯的第一个开源项目「Tinker」
- 基于HAL库的32流水灯
- 89600 matlab,【图片】使用Matlab分析频谱仪I/Q数据【matlab吧】_百度贴吧
- image not loaded try to open it externally to fix format problem
- 普通网站的建设和维护费用大概是多少?
- 2.7 zio入门——更多的Effect构造函数
- Oracle PL / SQL – UPDATE触发器之前的示例
- 设计模式 - 创建型模式_工厂方法模式
- wpf 的 Window或UserControl绑定自己后台属性
- 跟我学c++初级篇——别名的使用
- 一晚迎来十三年里最大单日涨幅,告别2021,中概股终于触底?
- 可以作为艺术作品欣赏的CT三维重建技术。
- 阿西莫夫机器人三原则
热门文章
- 哪个银行的信用卡好,教你如何选择信用卡!
- 全栈开发 乾坤大挪移
- 计算机与我们的工作 生活的影响吗,人工智能在未来对我们的工作、生活将带来的影响...
- 前端实现红包雨功能_最全解密微信红包随机算法(含代码实现)
- 【京东飞天茅台1499抢购】Python 脚本的完整安装、使用教程与解决方案
- DPOS——授权股权证明机制
- 宝藏又小众的飞机cc0高清摄影图片素材网站分享
- 《Python程序设计(第3版)》[美] 约翰·策勒(John Zelle) 第 10 章 答案
- python的shape函数的用法
- python打开csv文件设定分隔符_Python读取csv文件分隔符设置方法