采坑记录-Redis使用scan代替keys

[提前声明]
文章由作者:张耀峰 结合自己生产中的使用经验整理,最终形成简单易懂的文章
写作不易,转载请注明,谢谢!
spark代码案例地址: https://github.com/Mydreamandreality/sparkResearch


线上问题
  • 定时任务通过keys* 通配符匹配对应的key
  • 在这段时间内的其它服务(需要用到Redis)告警,无法进行正常服务
  • 在运维平台查看日志:服务告警这段时间内的请求全部抛出redis连接超时,服务中设置的redis超时时间为200ms
问题定位
  • 首先确定是keys命令引起的
  • 而且Redis中数据量要特别大,否则是不会触发这个bug的,之前数据量小的时候根本没有这个问题
  • 其次keys命令本身在redis中数据量特别大的情况下(百万及以上)就会特别慢
  • 最致命的是这个命令会阻塞redis多路复用的io主线程,如果这个线程被阻塞了,在这个期间其它服务到redis的请求会全部被阻塞,导致一系列反应,响应卡顿,连接超时等等
问题解决
  • 在线上环境,这种会阻塞主线程的操作,并且算法时间复杂度是O(n)的就应该禁止使用
替代方案
  • redis.scan
scan如何解决线上问题
  • scan和keys都是O(n)复杂度
  • scan同样支持通配字符模糊查找
  • scan不会阻塞主线程
  • scan支持游标按批次迭代返回数据
    注:scan返回的数据有可能会重复,需要客户端主动去重
scan注意事项
  • scan命令的游标从0开始从0结束
  • scan命令每次返回的数据都会携带下次游标所需的值,根据这个值再进行下一次的访问,如果返回的数据为空,不一定是没有数据了,需要通过游标来确认
scan命令使用案例

scan 0 match my*key count 10000

JavaRedisApi-scan命令使用案例
    public Set<String> scan(String key, long count) {
        Set<String> keys = redisTemplate.execute(
                (RedisCallback<Set<String>>) connection -> {
                    Set<String> keyTmp = new HashSet<>();
                    Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(key + "*").count(count).build());
                    while (cursor.hasNext()) {
                        keyTmp.add(new String(cursor.next()));
                    }
                    return keyTmp;
                });
        return keys;
    }
回归测试结果
  • 服务正常执行
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值