关于redis的keys命令的性能问题
查所有符合给定模式 pattern 的 key 。
KEYS * 匹配数据库中所有 key 。
KEYS h?llo 匹配 hello , hallo 和 hxllo 等。
KEYS h*llo 匹配 hllo 和 heeeeello 等。
KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo
实际应⽤中有时候会出现需要遍历redis中的所有键值的需求,⽐如清理没⽤的键等等。但是keys这个命令性能真的很差,redis官⽅⽂档是这么说的:
Warning: consider KEYS as a command that should only be used in production environments with extreme care.
It may ruin performance when it is executed against large databases.
redis五种数据结构
This command is intended for debugging and special operations, such as changing your keyspace layo
ut.
Don’t use KEYS in your regular application code. If you’re looking for a way to find keys in a subset of your keyspace,
consider using SCAN or sets.
Keys模糊匹配,请⼤家在实际运⽤的时候忽略掉。因为Keys会引发Redis锁,并且增加Redis的CPU占⽤,情况是很恶劣的,实际应⽤中有时候会出现需要遍历redis中的所有键值的需求,⽐如清理没⽤的键等等。但是keys这个命令性能真的很差。上⾯是官⽅⽂档声明,KEYS命令不能⽤在⽣产的环境中,这个时候如果数量过⼤效率是⼗分低的。同时也不要⽤KEYS正则匹配,官⽅建议直接⽤集合类型。
有⼈说 KEYS相当于关系性数据的库的 select *,在⽣产环境⼏乎是要禁⽤的。
KEYS命令的性能随着数据库数据的增多⽽越来越慢
KEYS命令会引起阻塞,连续的 KEYS命令⾜以让 Redis 阻塞
试想如果Redis阻塞超过10秒,如果有集的场景,可能导致集判断Redis已经故障,从⽽进⾏故障切换;以上的情况严重会导致应⽤程序出现雪崩的情
况。然⽽,⽹上很多都是这么写的 redis-cli --raw keys "key前缀*" | xargs redis-cli del,千万别照炒,拿到⽣产环境上做实验。(实际应⽤中有时候会出现需要遍历redis中的所有键值的需求,⽐如清理没⽤的键等等。但是keys这个命令性能真的很差)。顺便普及下xargs命令,这是Linux下的⼀个命令。
xargs命令是给其他命令传递参数的⼀个过滤器,也是组合多个命令的⼀个⼯具。它擅长将标准输⼊数据转换成命令⾏参数,
xargs能够处理管道或者stdin并将其转换成特定命令的命令参数。xargs也可以将单⾏或多⾏⽂本输⼊转换为其他格式,例如多⾏变单⾏,单⾏变多⾏。
xargs的默认命令是echo,空格是默认定界符。这意味着通过管道传递给xargs的输⼊将会包含换⾏和空⽩,不过通过xargs的处理,换⾏和空⽩将被空格取代。xargs是构建单⾏命令的重要组件之⼀。
由于执⾏keys命令,redis会锁定,如果数据庞⼤的话可能需要⼏秒或更长,对于⽣产服务器上锁定⼏秒这绝对是灾难了。如果有这种需求的话可以⾃⼰对键值做索引,⽐如把各种键值存到不同的set⾥⾯,分类建⽴索引,这样就可以很快的得到数据,但是这样也存在⼀个明显的缺点,就是浪费宝贵的空间,要知道这可是内存空间啊,所以还是要合理考虑,当然也可以想办法,⽐如对于有规律的键值,可以存储他们的始末值等等。
使⽤redis的时候要注意很多细节,当时的leader说过⼀句话很受启发,虽然redis只提供了五种类型,但是⽤起来不⼀定就只有五种,⽐如string类型,你可以存储任何你⾃⼰定义的类型,所以思想不能局限,灵活的设定数据结构。还有就是虽然redis存取很快,但是正常⽣产环境,redis服务器肯定和web服务器不是在⼀起,有时候甚⾄是在不同的地区,所以⽹络通信延迟就很重要了,所以要减少存取次数,⼀次存取完成更多的⼯作,否则你会发现做同样的事redis还没有关系型数据库快,所以redis存的时候⼀定要有技巧,尽可能减少存取次数。
从redis的官⽅⽂档上看,2.8版本之后SCAN命令已经可⽤,允许使⽤游标从keyspace中检索键。对⽐KEYS命令,虽然SCAN⽆法⼀次性返回所有匹配结果,但是却规避了阻塞系统这个⾼风险,从⽽也让⼀些操作可以放在主节点上执⾏。
需要注意的是,SCAN 命令是⼀个基于游标的迭代器。SCAN 命令每次被调⽤之后,都会向⽤户返回⼀个新的游标,⽤户在下次迭代时需要使⽤这个新游标作为SCAN 命令的游标参数,以此来延续之前的迭代过程。同时使⽤SCAN,⽤户还可以使⽤keyname模式和count选项对命令进⾏调整。SCAN相关命令还包括SSCAN命令、HSCAN 命令和 ZSCAN 命令,分别⽤于集合、哈希键及有续集等。
另⼀⽅⾯,使⽤redis的时候⼀定要注意控制key,对于key的命令要制定⼀个完善的⽅案,这样才能对redis⾥⾯的数据可控,避免出现没⽤数据长时间占据数据库这种情况,也可以避免上⾯说的这种查询键值的操作。
SCAN 命令
Redis从2.8版本开始⽀持scan命令,SCAN命令的基本⽤法如下:复杂度虽然也是O(n),通过游标分步进⾏不会阻塞线程;有限制参数COUNT;同 keys命令⼀样提供模式匹配功能;服务器不需要为游标保存状态,游标的唯⼀状态就是scan返回给客户端的游标整数;
scan⽤法
SCAN cursor [MATCH pattern] [COUNT count]
scan 命令提供三个参数,第⼀个是cursor,第⼆个是要匹配的正则,第三个是单次遍历的槽位
第⼀个遍历是 cursor 值为0,然后将返回结果的第⼀个整数作为下⼀个遍历的游标,如果最后返回的到cursor的值为0就代表结束。
127.0.0.1:6379> scan 0 MATCH tony*
1) "42"
2)  1) "tony25"
2) "tony2519"
3) "tony2529"
4) "tony2510"
5) "tony2523"
6) "tony255"
7) "tony2514"
8) "tony256"
9) "tony2511"
10) "tony15"
127.0.0.1:6379> scan 42 MATCH tony* COUNT 1000
1) "0"
2)  1) "tony3513"
2) "tony359"
3) "tony4521"
4) "tony356"
5) "tony30"
6) "tony320"
7) "tony3"
8) "tony312"
返回分为两个部分如上⾯的代码中, 1)代表下⼀次迭代的游标,2)代表本次迭代的结果集,注意如果返回游标为0就代表全部匹配完成。
批量删除scan命令
因为KEYS命令的时间复杂度为O(n),⽽SCAN命令会将遍历操作分解成m次,然后每次去执⾏,从⽽时间复杂度为O(1)。也解决使⽤keys命令遍历⼤量数据⽽导致Redis服务器阻塞的情况。所以建议使⽤下边的指令进⾏批量的删除操作:
redis-cli --scan --pattern "key前缀*" | xargs -L 1000 redis-cli del
总结
因为Redis是单线程的KEYS在某种情况下会阻塞。有个真实真案件⼩哥哥⽣产⽤KEYS,最终导致服务宕机。后果很严重,产⽣的经济损失就不说了。切记严重会导致程序的雪崩,删除的时候⽤SCAN命令,看完这篇⽂章应该都记住了。
Redis开发的建议
1、数据分离:不要什么都往Redis中放,尽量放些QPS⽐较⾼的数据,内存的开销很昂贵的,可以考虑硬盘存放。
2、分业务:不同的实例单独放这样存取的时候⽅便些,故障的时候也不会影响其他的实例。
3、压缩:redis中有很⼤的单个key的值建议压缩成⼆进制存放。
4、失效时间:redis中设置key的失效时间,如果不设置会⼀直占⽤着内存,⽽且key的失效时间应该根据业务场景来设置。
5、容量:占⽤内存不要太⼤10-20G,其次键的数量控制在1千万以内。
6、监控:运维合理的监控好数据,做好Redis安全漏洞的防护和灾备。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。