redis删除缓存时遇到的问题

一、redis查询key的方式

  redis常用两种方式用于key的精确/模糊匹配

 

  1. KEYS pattern

 

    keys pattern用于匹配pattern所有key,会返回当前库里所有匹配上pattern的key,其时间复杂度为O(N),会遍历库中所有key进行匹配,并且没有数量限制,当数据库的数据量较大时,可能会造成数据库阻塞(redis单线程),该命令通常用于debug或其他特殊场景不适合用于项目编码中。

   有两个很明显的缺点

  • 没有 offset、 limit 参数,一次性吐出所有满足条件的 key ,如果实例中有非常多 key 满足条件,很难找出你需要的有效数据。
  • keys 算法是遍历算法,复杂度是 O(n) ,如果实例中有千万级以上的 key ,这个指令就会导致 Redis 服务卡顿,所有读写 Redis 的其他指令都会被延后甚至会超时报错,因为 Redis 是单线程程序,顺序执行所有指令,其他指令必须等到当前的 keys 指令执行完了才可以继续。

  

  2. SCAN cursor [MATCH pattern] [COUNT count] [TYPE type](type可能不支持)

 

  2.8版本中引入了scan,与keys相比:

  • 每次调用scan的时间复杂度为O(1),遍历整个库调用的时间复杂度为O(N),但它是通过游标分步进行的,不会像keys命令一样阻塞redis的线程
  • 提供 limit 参数,可以控制每次返回结果的最大条数, limit 只是个 hint, 返回的结果可多可少
  • 同keys 一样,它也提供模式匹配功能。
  • 服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数。
  • 返回的结果可能会有重复,需要客户端去重,这点非常重要。
  • 遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的
  • 单次返回的结果是空的并不意昧着遍历结束,而要看返回的游标值是否为零。

  基本用法:第一次遍历时, cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor ,一直遍历到返回的 cursor 值为 0 时结束。

 

  字典的结构

  它是一维数 组,是二维链表结构。第一维数组的大小总是 2的n次方 (n>=0 ),扩容一次数组,大小空间加倍, 也就是 2的n次方加1。 scan 指令返回的游标就是第一维数组的位置索引,我们将这个位置索引称为槽( slot )。

  scan 遍历顺序

  采用了高位进位加法来遍历,是为了考虑到字典的扩容和缩容时避免槽位的遍历重复和遗漏。高位进位加法从左边加,进位往右边移动,同普通加法正好相反。

  字典扩容

  假设开始槽位的二进制数是 xxx ,那么该槽位中的元素将被 rehash到 0xxx 1xxx(xxx+8)中。如果字典长度由 16 位扩容到 32 位,那么对于二进制槽位 xxxx 中的元素将被 rehash 0xxxx和1xxxx(xxxx+ 16)中

  渐进式 rehash

  它会同时保留旧数组和新数组,然后在定时任务中以及后续对 hash 的指令操作中渐渐地将旧数组中挂接的元素迁移到新数组上。

  更多的 scan 指令

  如 zscan 遍历 zset 集合元素, hscan 遍历 hash 字典的元素, sscan 遍历 set 集合的元素。

  大 key 扫描

  在平时的业务开发中,要尽量避免大 key 的产生

  如何定位大 key 呢? 为了避免给线上 Redis 带来卡顿,就要用到 scan 指令,对于扫描出来的每一个 key ,使用 type 指令获得 key 的类型,然后使用相应数据结构的 size 或者 len 方法来得到它的大小,对于每一种类型,将大小排名的前若干名作为扫描结果展示出来。

    

 

posted @ 2022-08-25 16:15  重生之我是java程序员  阅读(282)  评论(0编辑  收藏  举报