Cache Usage


在具体使用缓存之前需要先了解一下缓存的一些配置、使用方式已经常见问题等,便于在应用中更加合理和高效的利用缓存。

缓存特征

缓存其实是一种数据模型,我们需要关注使用过程中的一些配置或参数。

缓存命中率

命中率问题是缓存中的一个非常重要的问题,它是衡量缓存有效性的重要指标。命中率越高,表明缓存的使用率越高。而 Caffeine(Guava)和 Redis(info 命令)都提供了相应的统计功能。

其中 Caffeine 的统计参考 这里,可以提供一下几项参数:

  • hitRate(): returns the ratio of hits to requests
  • evictionCount(): the number of cache evictions
  • averageLoadPenalty(): the average time spent loading new values

Redis 的统计可以使用 info 命令进行查看,返回的统计信息很多,可以利用其中的 hits 和 miss 计算缓存命中率:hits/(hits+miss)

keyspace_hits:14414110
keyspace_misses:3228654
used_memory:433264648
expired_keys:1333536
evicted_keys:1547380

另外还有 Redis 统计可视化的开源工具可以使用,更加直观方便,例如 redis-stat。不过大公司一般都会有专门的中间件团队去维护并提供集群的监控信息。

缓存最大空间

缓存中可以存放的最大元素的数量,一旦缓存中元素数量超过这个值(或者缓存数据所占空间超过其最大支持空间),那么将会触发缓存启动清空策略根据不同的场景合理的设置最大元素值往往可以一定程度上提高缓存的命中率,从而更有效的利用缓存。

例如在 Guava 或者 Caffeine 中,通过参数 spring.cache.guava.spec=maximumSize=500,expireAfterAccess=600s 或者 spring.cache.caffeine.spec=maximumSize=500,expireAfterAccess=600s 设置基于大小的缓存驱逐策略,同时还可以指定基于时间的驱逐策略。更多详细内容参考 这里

清空策略

如上描述,缓存的存储空间有限制,当缓存空间被用满时,如何保证在稳定服务的同时有效提升命中率?这就由缓存清空策略来处理,设计适合自身数据特征的清空策略能有效提升命中率。常见的一般策略有:

  • FIFO(first in first out)
    先进先出策略,最先进入缓存的数据在缓存空间不够的情况下(超出最大元素限制)会被优先被清除掉,以腾出新的空间接受新的数据。策略算法主要比较缓存元素的创建时间。在数据实效性要求场景下可选择该类策略,优先保障最新数据可用。

  • LFU(less frequently used)
    最少使用策略,无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间。策略算法主要比较元素的 hitCount(命中次数)。在保证高频数据有效性场景下,可选择这类策略。

  • LRU(least recently used)
    最近最少使用策略,无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素释放空间。策略算法主要比较元素最近一次被 get 使用时间。在热点数据场景下较适用,优先保证热点数据的有效性。

除此之外,还有一些简单策略比如:

  • 根据过期时间判断,清理过期时间最长的元素;
  • 根据过期时间判断,清理最近要过期的元素;
  • 随机清理;
  • 根据关键字(或元素内容)长短清理等。

缓存的数据一致性

因为缓存属于持久化数据的一个副本,因此不可避免的会出现数据不一致问题,导致脏读或读不到数据的情况。数据不一致,一般是因为网络不稳定或节点故障导致。根据数据的操作顺序,主要有以下几种情况。

先写缓存,再写数据库

这种情况时假如缓存写成功,但写数据库失败或响应延迟,则下次读取(并发读)缓存时,就出现脏读。这个写缓存的方式,本身就是错误的

先写数据库,再写缓存

这种是最常见的处理方式,但是如果写库成功写缓存失败时,则下次读取(并发读)缓存时,会读到未过期的脏数据或者需要回源。

缓存异步刷新

数据库操作和写缓存不在一个操作步骤中,比如在分布式场景下,无法做到同时写缓存或需要异步刷新(补救措施)时候。此种情况,主要考虑数据写入和缓存刷新的时效性。比如多久内刷新缓存,不影响用户对数据的访问。在类似商品评价列表这种业务场景中,实时性要求不是很高但可用性要求特别高,为了避免流量洪峰回源对数据库造成压力可以采用这个异步刷新持久化缓存的方式。

缓存雪崩问题

雪崩是指当大量缓存失效时,导致大量的请求访问数据库,导致数据库服务器,无法抗住请求或挂掉的情况。

常见处理方式:

  • 合理规划缓存的失效时间;
  • 合理评估数据库的负载压力;
  • 对数据库进行过载保护或应用层限流;
  • 多级缓存设计,缓存高可用。

缓存穿透问题

缓存一般是 Key value 方式存在,当某一个 Key 不存在时会查询数据库,假如这个 Key,一直不存在,则会频繁的请求数据库,对数据库造成访问压力。

常见处理方式:

  • 对结果为空的数据也进行缓存,当此 key 有数据后,清理缓存;
  • 一定不存在的 key,采用布隆过滤器,建立一个大的 Bitmap 中,查询时通过该 bitmap 过滤。

缓存架构

典型的缓存架构如图所示,其中职责划分:

  • CDN:存放HTML、CSS、JS等静态资源;
  • 反向代理:动静分离,只缓存用户请求的静态资源;
  • 分布式缓存:缓存数据库中的热点数据;
  • 本地缓存:缓存应用字典等常用数据。

参考

Copyright © jverson.com 2018 all right reserved,powered by GitbookFile Modify: 2019-03-03 22:26:44

results matching ""

    No results matching ""