BigKey: 用户越多,redis数据越多,bigkey会使得缓存数据更大,网络带宽会被占用,执行效率就低下,高并发的时候吞吐量QPS也会下降。
产生原因:
看如下list:
- 一个key的内容太大,比如1M,2M,这已经算很大了,有些公司的规范是最大不能超过512K
- 成员数太多,有写集合有数量限定,比如zset为10000个
- 集合中的嵌套属性太多,总大小甚至能超过10M
注意:bigkey不仅仅是value的大小,而是一个整体,key的大小,value的数据额结构,也都包含,越复杂的数据结构,那么所占用的空间也会更大。
引发问题:
bigkey 会越来越大,也会占用内存资源,此外由于redis的过期策略(LRU命中率低下则清除对应的key),key有很多都属于set集合,里面相应的键值可能会自动被清楚,会有大多数 key会被动删除,并且频繁的IO处理会影响性能,而且如此也可能出现缓存击穿的现象。
并且也会导致并发的负载不均衡(也就是流量倾斜),因为你是一个bigkey,他只是一个key,这个可以会存在redis汲取中的某个节点上,那么并发上来了,请求全部落在这集群的某一个节点上,而且不会把请求路由到集群的其他节点,因为这个bigkey是固定的。
此外数据查询也会越来越慢,性能也会降低,因为这个值越来越庞大,带宽占用也会越来越高,传输速度就慢了,甚至可能会导致网络的阻塞。
还有一点,bigkey越大,那么响应的,CPU其实也会需要计算的,因为有序列化和反序列化的过程,所以CPU的使用率也会上升,从而影响Redis的性能,甚至影响当前整个云主机节点的性能以及其他应用程序。
解决方案:
-
对bigkey进行瘦身:页面要什么数据就写什么数据,不要把所有的你觉得有用的数据写到redis中,你觉得有用的不代表当前业务就需要。如果存在嵌套的list(list做为key的某个属性),如果当前业务用不到,或者是其他页面所需要的,则直接删除,等有需要了再进行查询,可以拆为2个redis缓存来做。
-
压缩数据:查询出来的value很大,也可以通过压缩的算法,直接对数据压缩后在返回到前端,这是一种代码0侵入的方案。压缩算法:LZ4/Snappy。
-
减少set集合的长度,我们不要把全量的内容都作为一个list,因为没必要,在某些页面上显示的时候只需要显示10个20个,顶多100个就够了,所以业务层需要判断处理,这个set集合不能超过100个,超过100就需要优化,把优先级低的或者不怎么用的直接移除即可,比如根据时间排序,根据点赞数排序等等都行。
-
多级缓存:在业务层网关层加上缓存,如此就不需要读取redis-server了,如此在业务层或网关层可以直接返回当前的缓存数据即可,减少请求链路流通,带宽占用率就会降低,但是多个地方使用缓存,务必要做到缓存数据的一致性(一定要实现可以考虑弱一致)
-
拆分集合:比如现在定义这个set集合不能存太多,需要拆为几个子set,合并的set才是总set,那么现在有几千甚至上万的key,则此时我们可以对key进行哈希取模,我们可以定义10个或20个set,取模后根据结果来决定哪个key放入哪个set中,如此这样的set就体积变小了,我们要取数据的时候也根据取模后的结果来获得对应的值即可。这样的做法也能避免缓存数据在集群节点中的倾斜问题。
作业
- 自己分析现有的key哪些可能会存在bigkey,以提前做好预案或优化。
- 针对目前的key存储,大多是把对象作为字符串的形式去存储的,这样可以,但不够好,建议作为hash存储,把这个作为优化方案进行修改。