redis查询所有key命令_想在生产搞事情?那试试这些 Redis 命令

8a085b9819394b4bc41e9a41a7a3772a.png

作者:鸭血粉丝

出自:Java极客技术

原文:mp.weixin.qq.com/s/WeAamgYYGQfxlsppsn9_lg


哎,最近阿粉又双叒叕犯事了。

事情是这样的,前一段时间阿粉公司生产交易偶发报错,一番排查下来最终原因是因为 Redis 命令执行超时。

可是令人不解的是,生产交易仅仅使用 Redis set 这个简单命令,这个命令讲道理是不可能会执行这么慢。

那到底是什么导致这个问题那?

为了找出这个问题,我们查看分析了一下 Redis 最近的慢日志,最终发现耗时比较多命令为 keys XX*

看到这个命令操作的键的前缀,阿粉才发现这是自己负责的应用。可是阿粉排查一下,虽然自己的代码并没有主动去使用 keys命令,但是底层使用框架却在间接使用,于是就有了今天这个问题。

问题原因

阿粉负责的应用是一个管理后台应用,权限管理使用 Shiro 框架,由于存在多个节点,需要使用分布式 Session,于是这里使用 Redis 存储 Session 信息。

由于 Shiro 并没有直接提供 Redis 存储 Session 组件,阿粉不得不使用 Github 一个开源组件 shiro-redis。

由于 Shiro 框架需要定期验证 Session 是否有效,于是 Shiro 底层将会调用 SessionDAO#getActiveSessions 获取所有的 Session 信息。

shiro-redis 正好继承 SessionDAO 这个接口,底层使用用keys 命令查找 Redis 所有存储的 Session key。

public Set<byte[]> keys(byte[] pattern){checkAndInit();Set<byte[]> keys = null;Jedis jedis = jedisPool.getResource();try{keys = jedis.keys(pattern);}finally{jedis.close();}return keys;
}

找到问题原因,解决办法就比较简单了,github 上查找到解决方案,升级一下 shiro-redis 到最新版本。

在这个版本,shiro-redis 采用 scan命令代替 keys,从而修复这个问题。

public Set<byte[]> keys(byte[] pattern) {Set<byte[]> keys = null;Jedis jedis = jedisPool.getResource();try{keys = new HashSet<byte[]>();ScanParams params = new ScanParams();params.count(count);params.match(pattern);byte[] cursor = ScanParams.SCAN_POINTER_START_BINARY;ScanResult<byte[]> scanResult;do{scanResult = jedis.scan(cursor,params);keys.addAll(scanResult.getResult());cursor = scanResult.getCursorAsBytes();}while(scanResult.getStringCursor().compareTo(ScanParams.SCAN_POINTER_START) > 0);}finally{jedis.close();}return keys;}

虽然问题成功解决了,但是阿粉心里还是有点不解。

为什么keys 指令会导致其他命令执行变慢?

为什么Keys 指令查询会这么慢?

为什么Scan 指令就没有问题?

Redis 执行命令的原理

首先我们来看第一个问题,为什么keys 指令会导致其他命令执行变慢?

回答这个问题,我们首先看下 Redis 客户端执行一条命令的情况:

9463b60f78a8a0aca4fbabdbb648feff.png

站在客户端的视角,执行一条命令分为三步:

  1. 发送命令
  2. 执行命令
  3. 返回结果

但是这仅仅客户端自己以为的过程,但是实际上同一时刻,可能存在很多客户端发送命令给 Redis,而 Redis 我们都知道它采用的是单线程模型。

为了处理同一时刻所有的客户端的请求命令,Redis 内部采用了队列的方式,排队执行。

6030dead25f0d92b37c16d409bb49757.png

于是客户端执行一条命令实际需要四步:

  1. 发送命令
  2. 命令排队
  3. 执行命令
  4. 返回结果

由于 Redis 单线程执行命令,只能顺序从队列取出任务开始执行。

只要 3 这个过程执行命令速度过慢,队列其他任务不得不进行等待,这对外部客户端看来,Redis 好像就被阻塞一样,一直得不到响应。

所以使用 Redis 过程切勿执行需要长时间运行的指令,这样可能导致 Redis 阻塞,影响执行其他指令。

KEYS 原理

接下来开始回答第二个问题,为什么Keys 指令查询会这么慢?

回答这个问题之前,请大家回想一下 Redis 底层存储结构。

131052e50893ed2c7e5178cd471909c9.png

keys命令需要返回所有的符合给定模式 pattern 的 Redis 中键,为了实现这个目的,Redis 不得不遍历字典中 ht[0]哈希表底层数组,这个时间复杂度为 「O(N)」(N 为 Redis 中 key 所有的数量)。

如果 Redis 中 key 的数量很少,那么这个执行速度还是也会很快。等到 Redis key 的数量慢慢更加,上升到百万、千万、甚至上亿级别,那这个执行速度就会很慢很慢。

下面是阿粉本地做的一次实验,使用 lua 脚本往 Redis 中增加 10W 个 key,然后使用keys 查询所有键,这个查询大概会阻塞十几秒的时间。

eval "for i=1,100000  do redis.call('set',i,i+1) end" 0

SCAN 原理

最后我们来看下第三个问题,为什么scan 指令就没有问题?

这是因为 scan命令采用一种黑科技-「基于游标的迭代器」

每次调用 scan 命令,Redis 都会向用户返回一个新的游标以及一定数量的 key。下次再想继续获取剩余的 key,需要将这个游标传入 scan 命令, 以此来延续之前的迭代过程。

简单来讲,scan 命令使用分页查询 redis 。

下面是一个 scan 命令的迭代过程示例:

scan 命令使用游标这种方式,巧妙将一次全量查询拆分成多次,降低查询复杂度。

虽然 scan 命令时间复杂度与 keys一样,都是 「O(N)」,但是由于 scan 命令只需要返回少量的 key,所以执行速度会很快。

最后,虽然scan 命令解决 keys不足,但是同时也引入其他一些缺陷:

  • 同一个元素可能会被返回多次,这就需要我们应用程序增加处理重复元素功能。
  • 如果一个元素在迭代过程增加到 redis,或者说在迭代过程被删除,那个这个元素会被返回,也可能不会。

以上这些缺陷,在我们开发中需要考虑这种情况。

除了 scan以外,redis 还有其他几个用于增量迭代命令:

  • sscan:用于迭代当前数据库中的数据库键,用于解决 smembers 可能产生阻塞问题
  • hscan命令用于迭代哈希键中的键值对,用于解决 hgetall 可能产生阻塞问题。
  • zscan:命令用于迭代有序集合中的元素(包括元素成员和元素分值),用于产生 zrange 可能产生阻塞问题。

总结

Redis 使用单线程执行操作命令,所有客户端发送过来命令,Redis 都会现放入队列,然后从队列中顺序取出执行相应的命令。

如果任一任务执行过慢,就会影响队列中其他任务的,这样在外部客户端看来,迟迟拿不到 Redis 的响应,看起来就很阻塞了一样。

所以不要在生产执行 keyssmembershgetallzrange这类可能造成阻塞的指令,如果真需要执行,可以使用相应的scan 命令渐进式遍历,可以有效防止阻塞问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/431525.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql gui 分区_一文彻底搞懂MySQL分区

一.InnoDB逻辑存储结构首先要先介绍一下InnoDB逻辑存储结构和区的概念&#xff0c;它的所有数据都被逻辑地存放在表空间&#xff0c;表空间又由段&#xff0c;区&#xff0c;页组成。段段就是上图的segment区域&#xff0c;常见的段有数据段、索引段、回滚段等&#xff0c;在In…

js 获取域名_RapidDNS域名查询如何联动Goby

前言&#xff1a;http://RapidDNS.io 是一个秒级在线子域名和同IP域名的查询工具。目前拥有25亿条DNS记录&#xff0c;支持A、AAAA、CNAME、MX4种DNS记录类型。由于Goby程序对子域名收集方面不是很完善&#xff0c;这里特编写此插件作为其拓展。可以方便快速获取域名和ip地址信…

iOS 9应用开发教程之iOS 9新特性

iOS 9应用开发教程之iOS 9新特性 iOS 9开发概述 iOS 9是目前苹果公司用于苹果手机和苹果平板电脑的最新的操作系统。该操作系统于2015年6月8号&#xff08;美国时间&#xff09;被发布。本章将主要讲解iOS 9的新特性、以及使用Xcode 7.0如何编写一个简单的iOS 9的应用程序等内容…

kafka创建topic命令_0748-5.14.4-Kafka的扩容和缩容

​文档编写目的在Kafka集群资源使用已超出系统配置的资源时&#xff0c;或者有大量资源闲置造成资源浪费的时候&#xff0c;需要分别通过扩容Kafka和缩容Kafka来进行调整。本篇文章Fayson主要介绍如何进行Kafka的扩容和缩容&#xff0c;以及变更后的Kafka集群如何进行负载均衡的…

java 对话框 显示图片_Java对话框上显示图片

手掌心其实有很多种方法可以解决图片显示大小的问题:使用photoshop修改. 优点是可以节省系统资源, 显示图片的时候,不用做处理,缺点是需要了解ps的基本操作使用JDialog 自定义对话框. 优点 可以实现复杂的效果, 缺点,代码量比较多使用ImageIcon, Image 类 实现图片的缩放,. 优点…

class-dump获取iOS私有api

转自&#xff1a;http://blog.csdn.net/sunyuanyang625/article/details/41440167 获取各类iOS私有api 安装工具class&#xff0d;dump 资源地址http://download.csdn.net/detail/map625/8191343 运行class&#xff0d;dump并编译src项目 编译之后在produce中找到编译好的class…

火星云分发全网视频_好用的短视频一键分发软件,让工作效率提高10倍

随着近几年新媒体行业的高速发展&#xff0c;新媒体行业的红利也越来越来&#xff0c;也有越来越多的人想要享受到这波红利&#xff0c;于是不管是个人是企业都纷纷开始进入这个市场。不过也随之诞生了一系列麻烦繁琐的问题&#xff0c;如怎么持续创作内容&#xff0c;怎么花费…

java 采样_Java编程实现beta分布的采样或抽样实例代码

本文研究的主要是Java编程实现beta分布的采样或抽样&#xff0c;具体如下。本文将使用math3提供的工具包&#xff0c;对beta分布进行采样。如下程序是对alpha81&#xff0c;beta219的beta分布函数&#xff0c;进行抽样&#xff0c;共采样10000次。package function;/*** author…

树莓派 蓝牙音响_你应该拥有一个树莓派

为什么你应该拥有一个树莓派树莓派并不是极客的玩具树莓派可以用来做什么?它能实现的实在是太多了,最常见的比如自动化脚本 各种机器人bot: QQ/wechat/微博/facebook/telegram,IM结合爬虫 telegram的bot如今被黑产们结合比特币,做成了交易系统 使用宝塔linux面板,一键安装word…

训练集的识别率一直波动_机器学习验证集为什么不再有新意?

机器学习中&#xff0c;一般将样本数据分成独立的三部分&#xff1a;训练集、验证集和测试集。其中验证集在机器学习中所起到的作用是&#xff1a;开发模型总需要调节模型的参数&#xff0c;而整个调节过程需要在验证集数据上运行训练的模型&#xff0c;从而给出其表现的反馈信…

java写一个web聊天工具_javaweb写的在线聊天应用

写这个玩意儿就是想练练手&#xff0c; 用户需要登陆才能在线聊天&#xff0c;不要依赖数据库&#xff0c; 不需要数据库的操作&#xff0c; 所有的数据都是保存在内存中&#xff0c; 如果服务器一旦重启&#xff0c;数据就没有了&#xff1b;登录界面&#xff1a;聊天界面&…

人物角色群体攻击判定(一)

我们制作3D动作游戏的时候,常常主人公是一对多人进行攻击,或者敌人在角色前方一定范围内进行攻击. 我们怎么实现这种效果呢&#xff1f; 1. 使用触发器(多体攻击)方便调试,可视化(推荐)2. 运用点乘和叉乘,判断敌人的角度和位置(单,多)一般3. 使用Physics的Physics.OverlapSpher…

mfc cimage加载显示图片_在微信小程序里实现图片预加载组件

网页中的图片预加载我们知道在 Web 页面中实现图片的预加载其实很简单&#xff0c;通常的做法是在 JS 中使用 Image 对象即可&#xff0c;代码大致如下var image new Image() image.onload function() {console.log(图片加载完成) } image.src"//misc.360buyimg.com/lib…

HDU 2242 双连通分量 考研路茫茫——空调教室

思路就是求边双连通分量&#xff0c;然后缩点&#xff0c;再用树形DP搞一下。 代码和求强连通很类似&#xff0c;有点神奇&#xff0c;_&#xff0c;慢慢消化吧 1 #include <cstdio>2 #include <cstring>3 #include <algorithm>4 #include <vector>5 #…

java注册中心nacos_spring-cloud整合nacos做注册中心

前面讲了spring-cloud整合nacos做配置中心&#xff0c;这节讲一下nacos做注册中心至于注册中心是什么&#xff0c;我就不讲了&#xff0c;不了解的可以去问度娘下面我就直接开始了&#xff0c;也是比较简单的&#xff1a;1.引入依赖1.8Greenwich.SR12.1.0.RELEASEorg.springfra…

Android基于mAppWidget实现手绘地图(二)--概要

离线地图是一张被切成类似瓷砖般格子图像组成&#xff0c;这些被切开的格子组织成多个缩放级别。缩放级别是从0开始。0这个缩放级别&#xff0c;地图图像的尺寸为1*1像素。在每下一个缩放级别&#xff0c;图像尺寸则会两倍递增。 每个地图都是有层和地图对象的。 层是一个抽象的…

java实体字节属性定义_Java字节码方法表与属性表详解

而这些字节其实对应的信息是它&#xff1a;所以问题就来了&#xff0c;aload_0不是助记符信息么&#xff0c;怎么就能够跟字节码文件中的字节对应上呢&#xff1f;所谓助记符其实也就是帮忙我们去记忆的符合&#xff0c;在底层其实也是对应的一个个十六进制的数字的&#xff0c…

允许使用抽象类类型 isearchboxinfo 的对象_此对象非彼对象(面向对象)3

java13面向对象深入3大纲这里没有写面向对象-封装性理解封装( encapsulation)怎么去理解&#xff0c;一台电脑利用一个外壳包裹&#xff0c;保护了里面的电器元件&#xff0c;提供了少量的按键与其对接使用。我要看电视&#xff0c;只需要按一下开关和换台就可以了。有必要了解…

复制url直接能跳过验证_python 爬虫如何突破登录验证

我用 python 做爬虫爬过不少数据&#xff0c;比如在 google play 爬应用信息&#xff1b;在 instragram, 500px 爬图片&#xff1b;当然爬虫的作用不止于此&#xff0c;比如定时去某个网站签到&#xff0c;妈妈再也不用担心我忘记签到了这些网站支持游客访问&#xff0c;但要访…

[2-sat]HDOJ3062 Party

中文题 题意略 学2-sat啦啦啦 2-sat就是 矛盾的 ($x、x’$不能同时取) m对人 相互也有限制条件 取出其中n个人 也有可能是把一件东西分成 取/不取 相矛盾的两种情况 (那就要拆点啦~) 取其中n件 做法是 暴力 和 强连通 两种 重点在于建图&#xff1a; 对于x&#xff0c;记 取…