文章目录
- 1. 常见面试题
- 2. MoreKey案例
- 2. BigKey
- 2. BigKey生产调优
1. 常见面试题
- 海量数据里查询某一个固定前缀的key?
- 你如何生产上限制key * /flushdb /flushall等危险命令以防止误删误用?
- Memory Usage命令你用过吗?
- 多大算BigKey?你如何发现?如何删除?如何处理?
- BigKey你做过调优吗?惰性释放lazyfree你了解过吗?
- Morekey问题,生产上redis数据库有1000W条记录,你如何遍历?key*可以吗?
2. MoreKey案例
Morekey问题,生产上redis数据库有1000W条记录,你如何遍历?key*可以吗?
- 大批量往redis中出入测试数据
这里使用linux脚本往redis中出入100万条数据
for((i=1;i<=100*10000;i++)); do echo "set k$i v$i" >> ./redisTest.txt;done;
然后使用管道--pipe
命令插入100w大批量数据
cat /tmp/redisTest.txt /opt/redis-7.0.0/src/redis-cli -h 127.0.0.1 -p 6379 -a 111111 --pipe
由于我的redis是部署在docker中的,我直接用java程序向redis中写了100万条数据(速度会很慢)。
public class JedisTest {public static void main(String[] args) {//1.或的Connection,通过ip和断开Jedis jedis=new Jedis("127.0.0.1",6379);//2.指定访问redis的密码(我的redis没有设置密码)//jedis.auth("111111");//3.测试连接是否成功System.out.println(jedis.ping());//4.操作redisfor(int i=0;i<100*10000;i++){jedis.set("k"+i,"v"+i);}}
}
100万条数据插入完毕
下面使用keys *
查询一下所有数据
我们可以发现查询时间达到了恐怖的19s,这就是key *的致命弊端,在实际环境中不要用。由于这个指令没有offset、limit参数,是一次性吐出所有满足条件的key,由于redis是单线程的,其所有操作都是原子的,而keys *算法是遍历算法,复杂度是O(N),如果实例中有千万级以上的key,这个指令就导致redis服务卡顿,所有读写Redis的其它指令都会阻塞,可能会引起缓存雪崩甚至数据库宕机。
生产上如何限制keys * /flushdb /flushall等危险命令使用?
通过配置设置禁用这些命令,redis.conf在SECURITY这一项中
rename-command keys ""
rename-command flushdb ""
rename-command flushall ""
要想避免使用key*,同时又想遍历数据,此时就可以使用Scan
命令。该命令用于迭代数据库中的键,还衍生出了sscan(迭代集合的键),hscan(迭代哈希键)和zscan(迭代有序集合)
。
scan cursor [match pattern] [Count count]
- cursor:游标
- pattern:匹配的模式
- count:指定从数据集里返回多少元素,默认为10
该命令是基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程,以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历,不保证每次执行都返回某个给定数量的元素,他还支持模糊查询。该命令一次返回的数量不可控,只是大概率符合count参数。
scan返回一个包含两个元素的数组,第一个元素是用于下一次scan操作的新游标位置,第二个元素是一个数组,这个数组中包含了所有被迭代的元素,如果新游标返回0,则说明本次迭代结束
scan的遍历顺序非常特别,他不是从第一维度数组的0位一直遍历到末尾,而是采用了高位进位加法来遍历。之所以使用这种特殊的方法遍历,是考虑哈希槽的扩容和缩容时避免槽位的遍历重复和遗漏。解释
2. BigKey
多大算BigKey?你如何发现?如何删除?如何处理?
其实bigkey大的不是key本身,而是它对应的value
阿里云规范中说到,string类型控制在10kb以内,hash、list、set、zset元素个数不要超过5000。非字符串的我bigkey不要使用del删除,使用hscan、sscan、zscan方式渐进删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会出现在慢查询中)
bigkey会造成下面这些危害:
- 内存不均,集群迁移困难
- 超时删除,大key删除作梗
- 网络流程阻塞
大key产生通常不是人为的,如下面两个案例:
- 社交类:哥哥粉丝列表逐步递增
- 报表类:某个报表,月日年经年累月的积累
如何发现大key,有两个命令可以使用,首先是redis-cli --bigkeys
然后是Memory usage
#注意如果想查询10kb的所有key,这个命令就没什么用了
redis-cli --bigkeys
加上
-i 0.1s
这个参数,scan指令就会每隔100条scan就会休眠0.1s,ops(每s运算次数)就不会剧烈抖动,但是扫描时间会增加
# memory usage key [simples count]
Memory usage
该命令会给出一个key和它的值在RAM中占用的字节数,返回的结果是key的值以及为管理该key分配的内存总字节数。
如何删除bigkey,参考前面说到的阿里规范,我们使用使用hscan、sscan、zscan方式渐进删除。
- String类型的bigkey删除:一般使用del,如果过于庞大使用unlink异步删除
- Hash类型bigkey删除:使用hscan每次获取少量的field-value,再使用hdel删除每个field(一步一步掏空,最后删除)
- list类型:使用ltrim渐进式删除,直到全部删除完毕,思路和hash一样
- set类型:使用sscan每次获取部分元素,再使用srem命令删除每个元素
- Zset类型:使用zscan每次获取部分元素,再使用ZREMRANGEBYRANK命令删除每个元素
2. BigKey生产调优
BigKey你做过调优吗?惰性释放lazyfree你了解过吗?
redis.conf配置文件的LAZY FREEING部分可以对lazyfree进行配置,Redis有两个原语来删除键。一种称为del,是对象的阻塞删除,这意味着服务器停止处理新的命令,以便以同步方式回收与对象关联的所有内存,如果删除的键与一个小对象关联,则执行del命令所需的时间非常短,可与大多数其他命令相媲美。Redis还提供了非阻塞原语,例如UNLINK 以及FLUSHALL和FLUSHDB ASYNC选项,以便在后台回收内存,这些命令在恒定的时间内执行,另一个线程将尽可能快地释放后台中的对象。
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del yes
replica-lazy-flush nolazyfree-lazy-user-del yes
lazyfree-lazy-user-flush no