想在生产搞事情?那试试这些 Redis 命令

本文转载自微信公众号「Java极客技术 」,想生作者鸭血粉丝。产搞转载本文请联系Java极客技术 公众号。事情试
哎,那试最近阿粉又双叒叕犯事了。命令
事情是想生这样的,前一段时间阿粉公司生产交易偶发报错,产搞一番排查下来最终原因是事情试因为 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 客户端执行一条命令的情况:

站在客户端的视角,执行一条命令分为三步:
发送命令 执行命令 返回结果但是这仅仅客户端自己以为的过程,但是实际上同一时刻,可能存在很多客户端发送命令给 Redis,而 Redis 我们都知道它采用的是单线程模型。
为了处理同一时刻所有的客户端的请求命令,Redis 内部采用了队列的方式,排队执行。

于是客户端执行一条命令实际需要四步:
发送命令 命令排队 执行命令 返回结果由于 Redis 单线程执行命令,只能顺序从队列取出任务开始执行。
只要 3 这个过程执行命令速度过慢,队列其他任务不得不进行等待,这对外部客户端看来,Redis 好像就被阻塞一样,一直得不到响应。
所以使用 Redis 过程切勿执行需要长时间运行的指令,这样可能导致 Redis 阻塞,影响执行其他指令。
KEYS 原理
接下来开始回答第二个问题,为什么Keys 指令查询会这么慢?
回答这个问题之前,请大家回想一下 Redis 底层存储结构。b2b信息网
这里阿粉复制之前文章内容,Redis 底层使用字典这种结构,这个结构与 Java HashMap 底层比较类似。

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这里阿粉使用 Docker 部署 Redis,性能可能会稍差。
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 的响应,看起来就很阻塞了一样。
所以不要在生产执行 keys、smembers、hgetall、zrange这类可能造成阻塞的指令,如果真需要执行,可以使用相应的scan 命令渐进式遍历,可以有效防止阻塞问题。
相关文章
固态硬盘导入系统教程(使用固态硬盘(SSD)为你的电脑带来飞跃性能提升)
摘要:随着计算机技术的不断进步,固态硬盘SolidStateDrive,简称SSD)逐渐取代传统机械硬盘成为电脑存储设备的首选。本文将介绍如何使用固态硬盘导入系统,以及通过这一操作为你...2025-11-04- 摘要:随着科技的不断进步,个人电脑已成为现代生活中不可或缺的工具。而要让电脑发挥最佳性能,一个高效的处理器就显得至关重要。i57600处理器作为Intel第七代酷睿系列的成员之一,以其卓...2025-11-04
 手把手教你恢复删除的照片和视频(从误删除到重现珍贵回忆,无损恢复的最佳方法)
摘要:在现代社交媒体的盛行下,我们拍摄并分享的照片和视频变得越来越多。然而,不小心删除了一些珍贵的回忆品可能是每个人都经历过的痛苦。幸运的是,有一些有效的方法可以帮助我们从各种设备中恢复...2025-11-04如何查找已连接的手机WiFi密码(快速找回忘记的WiFi密码,轻松连接网络)
摘要:随着移动互联网的发展,WiFi已经成为我们生活中不可或缺的一部分。然而,我们经常会面临一个问题:当我们将手机连接到一个新的WiFi网络时,我们却忘记了之前连接的网络的密码。本文将教...2025-11-04探讨电脑插着U盘启动错误的原因及解决方法(分析电脑插着U盘启动错误的情况,掌握解决技巧)
摘要:在使用电脑过程中,有时候我们会遇到插着U盘启动出现错误的情况。这种问题可能会导致电脑无法正常启动,给我们的工作和生活带来不便。本文将从各个方面探讨电脑插着U盘启动错误的原因,并提供...2025-11-04以优能UP手机电池的革新(一种新一代超强续航手机电池,改变你的手机使用方式)
摘要:在现代社会中,手机已经成为人们生活不可或缺的一部分。然而,随着手机功能的不断升级和使用频率的增加,电池续航问题也变得日益突出。针对这一问题,以优能UP手机电池应运而生,为用户带来了...2025-11-04

最新评论