Redis有哪些慢操作?( 二 )


# 查询所有的keykeys *# 查询以book为前缀的keykeys book*keys命令的复杂度是O(n),它会遍历这个dict中的所有key,如果Redis中存的key非常多,所有读写Redis的指令都会被延迟等待,所以千万不用在生产环境用这个命令(如果你已经准备离职的话,祝你玩的开心) 。
「既然不让你用keys,肯定有替代品,那就是scan」
scan是通过游标逐步遍历的,因此不会长时间阻塞Redis
「用用zscan遍历zset,hscan遍历hash,sscan遍历set的原理和scan命令类似,因为hash,set,zset的底层实现的数据结构中都有dict 。」
操作bigkey「如果一个key对应的value非常大,那么这个key就被称为bigkey 。写入bigkey在分配内存时需要消耗更长的时间 。同样,删除bigkey释放内存也需要消耗更长的时间」
如果在慢日志中发现了SET/DEL这种复杂度不高的命令,此时你就应该排查一下是否是由于写入bigkey导致的 。
「如何定位bigkey?」
Redis提供了扫描bigkey的命令
$ redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01...-------- summary -------Sampled 829675 keys in the keyspace!Total key length in bytes is 10059825 (avg len 12.13)Biggest string found 'key:291880' has 10 bytesBiggestlist found 'mylist:004' has 40 itemsBiggestset found 'myset:2386' has 38 membersBiggesthash found 'myhash:3574' has 37 fieldsBiggestzset found 'myzset:2704' has 42 members36313 strings with 363130 bytes (04.38% of keys, avg size 10.00)787393 lists with 896540 items (94.90% of keys, avg size 1.14)1994 sets with 40052 members (00.24% of keys, avg size 20.09)1990 hashs with 39632 fields (00.24% of keys, avg size 19.92)1985 zsets with 39750 members (00.24% of keys, avg size 20.03)可以看到命令的输入有如下3个部分

  1. 内存中key的数量,已经占用的总内存,每个key占用的平均内存
  2. 每种类型占用的最大内存,已经key的名字
  3. 每种数据类型的占比,以及平均大小
这个命令的原理就是redis在内部执行了scan命令,遍历实例中所有的key,然后正对key的类型,分别执行strlen,llen,hlen,scard,zcard命令,来获取string类型的长度,容器类型(list,hash,set,zset)的元素个数
使用这个命令需要注意如下两个问题
  1. 对线上实例进行bigkey扫描时,为避免ops(operation per second 每秒操作次数)突增,可以通过-i增加一个休眠参数,上面的含义为,每隔100条scan指令就会休眠0.01s
  2. 对于容器类型(list,hash,set,zset),扫描出的是元素最多的key,但一个key的元素数量多,不一定代表占用的内存多
「如何解决bigkey带来的性能问题?」
  1. 尽量避免写入bigkey
  2. 如果使用的是redis4.0以上版本,可以用unlink命令代替del,此命令可以把释放key内存的操作,放到后台线程中去执行
  3. 如果使用的是redis6.0以上版本,可以开启lazy-free机制(lazyfree-lazy-user-del yes),执行del命令的时候,也会放到后台线程中去执行
大量key集中过期我们可以给Redis中的key设置过期时间,那么当key过期了,它在什么时候会被删除呢?
「如果让我们写Redis过期策略,我们会想到如下三种方案」
  1. 定时删除,在设置键的过期时间的同时,创建一个定时器 。当键的过期时间来临时,立即执行对键的删除操作
  2. 惰性删除,每次获取键的时候,判断键是否过期,如果过期的话,就删除该键,如果没有过期,则返回该键
  3. 定期删除,每隔一段时间,对键进行一次检查,删除里面的过期键 定时删除策略对CPU不友好,当过期键比较多的时候,Redis线程用来删除过期键,会影响正常请求的响应
定时删除策略对CPU不友好,当过期键比较多的时候,Redis线程用来删除过期键,会影响正常请求的响应
惰性删除读CPU是比较有好的,但是会浪费大量的内存 。如果一个key设置过期时间放到内存中,但是没有被访问到,那么它会一直存在内存中
定期删除策略则对CPU和内存都比较友好
redis过期key的删除策略选择了如下两种
  1. 惰性删除
  2. 定期删除
「惰性删除」 客户端在访问key的时候,对key的过期时间进行校验,如果过期了就立即删除
「定期删除」 Redis会将设置了过期时间的key放在一个独立的字典中,定时遍历这个字典来删除过期的key,遍历策略如下


推荐阅读