当前位置: 首页 > 后端技术 > Java

Reids的BigKey和HotKey

时间:2023-04-02 09:27:05 Java

1.什么是BigKey和HotKey1.1.BigKeyRedisbigkey问题其实不是bigkey问题,而是key对应的value太大了,所以严格来说是BigValue问题。Redisvalueistoolarge(键值太大)。多大的价值会引起大的关键问题,没有统一的标准。比如一个String类型的值,有时候超过5M就是大key,有时候超过10K就可以算Bigey,保险起见。BigKey会导致什么问题?1、由于数值较大,导致序列化和反序列化时间过长,网络延时也长。导致操作BigKey的时间过长,降低了Redis的性能。2、集群模式下,无法实现负载均衡,导致负载偏向某个实例。单实例的QPS会比较高,内存占用会比较大。3、由于Redis是单线程的,如果要删除这个大key,可能会导致正在操作的实例被阻塞,导致无法响应请求。大钥匙是怎么来的?一般来说,BigKey的产生是由于程序设计者对数据大小的预测不当,或者是设计考虑的疏漏造成的。在一些业务场景中,很容易产生BigKey,比如KOL或流量明星粉丝列表、投票统计、大规模数据缓存等。1.2.HotKey热键,也叫HotspotKey,即热键。如果某个特定的key突然有大量的请求,并且流量集中到某个instance,甚至因为物理网卡在线导致Redis服务器宕机,这个时候其实就是hotkey的问题。热键会导致很多系统问题:1.流量过度集中导致集群的优势无法发挥。如果达到实例的处理上限,节点就会宕机,进而影响数据库,造成缓存雪崩,导致整个系统挂掉。2、由于Redis是单线程运行,热键会影响示例中其他键的运行。2.如何发现BigKey和HotKey2.1。通过Redis命令发现BigKey1并查询BigKey。下面的命令可以扫描整个Redis的Key空间,寻找不同数据类型中最大的Key。-i0.1参数可以在扫描过程中每100条命令执行sleep0.1秒。Redis自带的bigkeys命令可以轻松在线扫描大key,对服务性能影响不大。唯一的缺点是信息量少,而且每种类型只有最大的key。$redis-cli-p999--bigkeys-i0.12。通过开源工具查询BigKey。使用开源工具的好处是获取的关键信息详细,可选参数多,支持定制需求,后续处理方便。缺点是需要离线操作,需要很长时间才能出结果。比如redis-rdb-tools等。$gitclonehttps://github.com/sripathikrishnan/redis-rdb-tools$cdredis-rdb-tools$sudopythonsetup.pyinstall$rdb-cmemorydump-10030.rdb>memory.csv2.2.foundHotKey1、hotkeys参数Redis在4.0.3版本增加了hotkeys(github.com/redis/redis...)搜索功能,可以直接使用redis-cli--hotkeys获取当前键空间的热键,并实现通过scan+objectfreq完成。2.monitor命令monitor命令可以实时抓取Redis服务器接收到的命令,通过redis-climonitor抓取数据,结合一些现成的分析工具,比如redis-faina统计热键。三、BigKey问题的解决寻找并解决BigKey问题,可以参考以下思路:1、在设计程序之初,预估值的大小,避免出现过大值的情况业务设计。2、通过监控,尽快找到大钥匙。3.如果无法避免大key,可以将一个key拆分成多个部分,分别存放在不同的key中。下面以List类型的v??alue为例,演示一下拆分解决大key问题的方法。有一个UserId列表,有1000万条数据。如果全部存储在一个Key下,会非常大,可以通过分页拆分来访问数据。下面是访问数据的代码实现:/***将用户数据写入Redis缓存**@paramuserIdList*/publicvoidpushBigKey(ListuserIdList){//将数据拆分为1000页intpageSize=1000;List>userIdLists=Lists.partition(userIdList,pageSize);//遍历所有页面,将每个页面数据保存到一个Key中,通过后缀索引来区分Longindex=0L;for(ListuserIdListPart:userIdLists){StringpageDataKey="user:ids:data:"+(index++);//使用pipeline管道减少连接数redisTemplate.executePipelined((RedisCallback)connection->{for(LonguserId:userIdListPart){connection.lPush(pageDataKey.getBytes(),userId.toString().getBytes());}返回null;});redisTemplate.expire(pageDataKey,1,TimeUnit.DAYS);}//保存数据后,将数据的页码保存在单独的KeyStringindexKey="user:ids:index";redisTemplate.opsForValue().set(indexKey,index.toString());redisTemplate.expire(在dexKey,1,TimeUnit.DAYS);}/***从Redis存储读取用户数据**@return*/publicListpopBigKey(){StringindexKey="user:ids:index";StringindexStr=redisTemplate.opsForValue().get(indexKey);如果(StringUtils.isEmpty(indexStr)){返回null;}ListuserIdList=newArrayList<>();长索引=Long.parseLong(indexStr);for(Longi=1L;i<=index;i++){StringpageDataKey="user:ids:data:"+i;LongcurrentPageSize=redisTemplate.opsForList().size(pageDataKey);ListdataListFromRedisOnePage=redisTemplate.executePipelined((RedisCallback)连接->{for(intj=0;j