Redis缓存命中率

losetowin 发布于:2019-10-22 12:50 分类:Java  有 208 人浏览,获得评论 0 条  

本文地址:https://www.dutycode.com/post-172.html
除非注明,文章均为 www.dutycode.com 原创,欢迎转载!转载请注明本文地址,谢谢。
  Redis命中率计算方式:

使用redis管理的 info 命令:

# Stats
total_connections_received:24548538
total_commands_processed:61226225746
instantaneous_ops_per_sec:4410
total_net_input_bytes:5714615648992
total_net_output_bytes:19327841733488
instantaneous_input_kbps:340.53
instantaneous_output_kbps:1349.35
rejected_connections:0
sync_full:2
sync_partial_ok:0
sync_partial_err:0
expired_keys:1156849643
evicted_keys:0
keyspace_hits:54422405751
keyspace_misses:2707567737
pubsub_channels:1
pubsub_patterns:0
latest_fork_usec:395856


keyspace_hits 表示命中次数, keyspace_misses表示未命中次数。
所以,命中率=keyspace_hits/(keyspace_hits+keyspace_misses)
上面系统的命中率也就是 54422405751/(54422405751+2707567737)=95%

  命中率多少合适?
    越高越好,倾向于95%以上。

  哪些影响命中率?
1、业务场景,是读多写少还是读少写多。
        读多写少,适合用缓存。

2、过期时间和缓存粒度的设定。
        过期时间设置过短时,缓存命中的可能就会变小。理论上,缓存的粒度越小,命中率越高。比如 缓存一个用户的一个工作地址信息,和缓存一个用户所有的工作地址信息列表相比,一个工作地址这种粒度的命中率会更高,因为更新的可能性小。但是对于一个列表而言,其中的一个工作地址变更,缓存信息就需要跟着变更,导致命中率就会下降。
    
3、容量的大小及失效策略配置
    maxmemory :表示当前实例的最大内存容量。(maxmemory_human)   32位系统,最大支持4G。当设置为0的时候(不限制大小),默认最大内存是3G,防止redis崩溃。
            当值为0的时候,表示不限制。
代码如下:
server.c

  maxmemory_policy :到达最大内容的时候的策略。
      1.volatile-lru(least recently used):最近最少使用算法,从设置了过期时间的键中选择空转时间最长的键值对清除掉.
      2.volatile-ttl:从设置了过期时间的键中选择过期时间最早的键值对清除;
      3.volatile-random:从设置了过期时间的键中,随机选择键进行清除;
      4.allkeys-lru:最近最少使用算法,从所有的键中选择空转时间最长的键值对清除;
      5.allkeys-random:所有的键中,随机选择键进行删除;
     6.noeviction:不做任何的清理工作,在redis的内存超过限制之后,大部分的写入操作(DEL除外,因为DEL不会造成内存增长)都会返回错误;但是读操作都能正常的进行;


    PS:volatile和allkeys的区别在于,volatile只淘汰设置了过期时间的key值,未设置过期时间的key值不会淘汰,如果没有设置过期时间的key值,效果等同与noeviction。allkeys淘汰的值范围则是所有的key值,不区分是否设置过过期时间。

LFU的过期策略是在redis4.0之后新增的策略,具体有以下两种。
    1.volatile-lfu(least frequently used):最近最不经常使用算法,从设置了过期时间的键中选择某段时间之内使用频次最小的键值对清除掉;
    2.allkeys-lfu:最近最不经常使用算法,从所有的键中选择某段时间之内使用频次最少的键值对清除;


当内存满了之后,根据淘汰策略设定,会淘汰一部分key值,所以可能导致命中率变低。当淘汰策略为noeviction的时候,虽然内存不被淘汰,读不受影响,但是因为写操作会失败,所以可能会产生命中率高的key无法写入,一定程度上也会影响到命中率。


  官方推荐的几种过期策略的设定:
        
    1、当请求符合幂律分布的时候,适合使用allkeys-lru策略。即:一部分数据会被频繁访问,或者说,大部分请求会命中在部分数据集上。类似于常说的28法则。
    2、如果请求命中的数据概率基本相当,适合用allkeys-random策略。即,每个数据被访问到的概率差不多,所以,随机淘汰数据,对整体的命中率影响不大
    3、如果想通过设置不同的ttl来过期数据,这种比较适合用volatile-ttl。即:业务方知道哪些对象适合什么时间淘汰。
    4、volatile-lru和volatile-random一般用在想用一个redis实例来实现缓存和持久化目的的场景,但是最好是将缓存场景和持久化场景分开。也就是使用两个不同的实例。PS:volatile-lru和volatile-random能做到这一点的原因在于,当redis中没有已经设置了过期时间的key值时,则相当于noeviction的策略了,也就是相当于持久化了。 
PS:给key值设置过期时间是要额外消耗一部分内存的, 所以,如果没有设置过期时间的诉求的话,推荐使用allkeys-lru的策略。可以更好提升内存的使用。 

  过期策略如何运行?
    1、客户端执行命令,并且这个命令执行后,会占用redis内存。
    2、redis执行命令前,会先检查下当前内存的大小是否超过设定的maxmemory值,如果超过,则按照淘汰策略,释放内存
    3、命令执行完成 (如果依旧没有内存,则执行失败,比如noevition策略下)

  过期策略:
server.c 在执行redis命令的时候,都会去检测下是否到达内存上限,如果达到,则按照过期策略释放内存。
方法:int processCommand(client *c)

evict.c里面的freeMemoryIfNeededAndSafe()方法,用来根据各种策略释放内存

注意:代码中有这么一段内容:

前面说,淘汰策略设置为noeviction的时候,缓存不会被释放。这段代码上看,redis还是会尝试去看下是否有可能会释放内存。 

can_free代码块中可以看到, 如果淘汰策略设置为noeviction的时候,当内存达到最大内存时,会去尝试看下是否存在lazy free thread(也就是延迟释放)线程中是否还有未执行完的释放任务。如果有的话,则等待内存释放。

lazy-free 在Redis4.0新增
关于 lazy free的介绍,可以看下:https://www.jianshu.com/p/e927e99e650d  

  如何提高命中率?
    
    根据原因不同,具体方式不同。
    1、如果是内存不够导致频繁淘汰内存,则需要增大最大内存配置,或者调整缓存的淘汰策略。 
    2、如果是粒度设计不合理,则更改粒度设计,使粒度变小。但是注意,粒度变小可能同时带来的问题会是内存变大。所以适当考虑对内容做压缩。 
    3、过期时间的设定,在业务允许的情况下,可以增加缓存的过期时间。 
    4、如果业务场景是访问并发很高,但对数据的时效性要求没有那么高的时候,可以考虑做缓存预热。如果并发度不是很高,访问比较分散的话, 缓存预热反而效果不好。 

版权所有:《攀爬蜗牛》 => 《Redis缓存命中率
本文地址:https://www.dutycode.com/post-172.html
除非注明,文章均为 《攀爬蜗牛》 原创,欢迎转载!转载请注明本文地址,谢谢。