一.Redis 真的变慢了吗?

查看Redis响应延迟:

  1. 延迟"毛刺"。
  2. 当你发现Redis命令的执行时间增长到几秒,基本就认定Redis变慢

存在问题

  • 不同软硬件环境下,Redis本身的绝对性能并不相同。

当前环境下Redis基线性能

  • 是指一个系统在低压力,无干扰的基本性能。这个性能由当前的软硬件配置决定。
  • 从2.8.7版本开始,redis-cli命令提供了-intrinsic-latency选项,可以用来监测和统计测试期间内的最大延迟,这个延迟可以作为Redis的基线性能。
  • 测试时长可以用-intrinsic-latency选项参数来指定。

例子:

 

./redis-cli --intrinsic-latency 120
Max latency so far: 17 microseconds.
Max latency so far: 44 microseconds.
Max latency so far: 94 microseconds.
Max latency so far: 110 microseconds.
Max latency so far: 119 microseconds.

36481658 total runs (avg latency: 3.2893 microseconds / 3289.32 nanoseconds per run).
Worst run took 36x longer than the average latency.

我们运行上面的命令,该命令会打印120秒 监测到的 最大延迟。可以看到,这里的最大延迟 是119微秒,也就是基线性能为119微秒。

 一般情况下,运行120秒就足够监测到最大延迟了,所以我们把参数设置为120。

 如何认定

你要把运行时延迟和基线性能进行对比,如果你观察到的 Redis 运行时延迟是其基线性能的 2 倍及以上,就可以认定 Redis 变慢了。

如何应对Redis变慢?

   1.自身操作特性的影响

      慢查询命令和过期 key 操作

  1. 慢查询命令,就是指在Redis中执行速度慢的命令,这会导致redis延迟增加。
  2. Redis提供的命令很多,并不是所有的命令都慢,这与命令操作的复杂度有关,所以我们必须要知道不同Redis的不同命令的复杂度。

     处理方式:

  1. 用其他高效命令替代,如果你需要返回一个SET中的所有成员,不要 使用smembers命令,而是要 使用SSCAN多次迭代返回,避免一次性返回大量 数据造成 线程阻塞。
  2. 当你需要执行排序,交集,并集操作时,可以在 客户端完成,不要用SORT,SUNION,SINTER这些命令,以免拖慢Redis实例。
  3. KEYS:它用于返回和输入模式 匹配所有的key:
 KEYS *name*
1) "lastname"
2) "firstname"

     KEYS命令需要遍历存储键值对 ,所以操作延时高,如果你不了解他的实现而使用会导致Redis性能变慢,所以,KEYS命令一般不建议用于生产环境。

2. 过期 key 操作    

Redis键值对的key可以设置 过期时间。默认情况下,Redis每100毫秒山出一些过期key,具体算法如下:

  1. 采样ACTIVE_EXPIRE_CYCLE_LOOKUPS_PRE_LOOP个数 的key,然后将其中过期的key全部删除 ;
  2. 如果超过25%的key过期了,则重复删除过程,直到过期key的比例降低至25%以下。
  3. ACTIVE)EXPIRE_CYCLE_LOOKUPS_PER_LOOP是Redis的一个参数,默认是20,那么,一秒内基本有200个过期key会被删除。这一策略对清楚过期 key,释放内存有帮助。
  4. 如果每秒删除200个过期key,并不会对Redis造成太大影响。

如果触发 上面超过百分之25key过期,Redis就会一直删除以释放内存空间,注意,删除操作时阻塞(Redis 4.0 后可以用异步线程机制来减少阻塞影响)。所以一旦改条件 触发,Redis的线程就会一直执行删除,就会无法正常服务其他键值对操作,进一步引起其他键值操作的延迟增加,Redis就会变慢。

产生原因:

    频繁使用带有相同时间参数 的EXPIRE命令设置过期key,这就会导致在同一秒内 有大量的key同时过期。

解决方案:

  1. 检查业务代码在使用EXPIREAT命令设置key过期时间时,是否使用了相同的UNIX时间戳
  2. 是否批量设置相同过期秒数,这都会造成大量key在同一时间过期,导致性能变慢。

 可以添加一个随机数时间,保证key在一个邻近时间范围内被删除,又避免同时过期造成的压力。

SCAN命令替代KEYS命令

  • 如果想要 获取整个实例的所有key,建议使用SCAN命令替代。
  • 客户端通过执行SCAN $cursor COUNT $count可以得到下一批key以及下一个游标$cursor
  • $cursor当作SCAN的参数,再次执行,以往往复,直到返回的$cursor为0时,就把整个实例中所有key遍历出来。

存在问题:Redis在做Rehash时候会不会漏key或者返回重复的key。

不会漏key会返回重复的key

  • 为什么不葫芦漏key,Redis在 SACAN遍历全局哈希表时候,采用 高位进位法的方法遍历哈希 桶,当哈希表扩容后,通过这种算法遍历,旧哈希表中的数据映射到新哈希表,依然会保留原来的先后顺序 ,这样就可以保证遍历时候不会遗漏也不会重复。
  • SCAN为什么会得到重复的key,这个情况主要发生在哈希表缩容,已经遍历过的哈希桶在缩容时候,会映射到新的哈希表没有遍历到的位置,所以继续遍历就会对同一个key返回多次。

使用HSCAN/SSCAN/ZSCAN命令,返回的元素数量与执行SCAN逻辑可能不同。执行SCAN $cursor COUNT $count时一次最多返回count个数的key,数量不会超过count。

但Hash/Set/Sorted Set元素数量比较少时,底层会采用intset/ziplist方式存储,如果以这种方式存储,在执行HSCAN/SSCAN/ZSCAN命令时,会无视count参数,直接把所有元素一次性返回,也就是说,得到的元素数量是会大于count参数的。当底层转为哈希表或跳表存储时,才会真正使用发count参数,最多返回count个元素。