专栏首页CodingToDie缓存 | 从本地缓存到分布式缓存, Guava, Caffeine, Memcached, Redis

缓存 | 从本地缓存到分布式缓存, Guava, Caffeine, Memcached, Redis

从本地缓存到分布式缓存

本文档中部分代码不保证可以运行

虽然标题为缓存,但在这里不仅仅会涉及缓存,还会涉及一些其他提高应用性能的方案。

在程序设计中,经常能听到的就是以时间换空间以空间换时间缓存作为一种能加快程序性能的银弹,它是典型的后者(以空间换时间).

随着用户数和访问量越来越大,我们的应用需要支撑更多的并发量,同时我们的应用服务器和数据库服务器所做的计算也越来越多。但是往往我们的应用服务器资源是有限的,数据库每秒能接受的请求次数也是有限的(或者文件的读写也是有限的),如何能够有效利用有限的资源来提供尽可能大的吞吐量?一个有效的办法就是引入缓存,每个环节中请求可以从缓存中直接获取目标数据并返回,从而减少计算量,有效提升响应速度,让有限的资源服务更多的用户。

缓存并不是包治百病的银弹

第一次接触缓存`MAP`

我第一次接触缓存的时候是在大三开始出去工作的时候。在一个系统中,基本每个接口都有可能要获取一次用户信息和一些用户配置,当时我们的系统查多改少,这也注定缓存可以大大提高我们的性能,当时的做法是维护一个全局的单例的Map作为缓存存储.记得当时的类名叫DBMirror

大致如下:

class DBMirror {
    private static Map<String, User> userCache = new HashMap<>();

    public static void putUser(String key, User user) {
        userCache.put(key, user);
    }

    public static User getUser(String key) {
        return userCache.get(key);
    } 

    private DBMirror() {}

}

代码很简单,基本满足了当时系统的要求,减少了很多数据库读写操作,在当时也是第一次开始意识到 数据库 并不是唯一的存储. 原来 Map 还能这样使用

但是上面的代码有个很大的缺点,随着用户的增多,里面并没有合适的剔除算法,会导致 Map 越来越大,极端情况会导致内存溢出

常见淘汰策略

如上所述,如果不使用剔除算法,会导致内存占用越来越大,且无法回收,那下面讲一下常见的淘汰策略

FIFO(first in first out)

先进先出策略,最先进入缓存的数据在缓存空间不够的情况下(超出最大元素限制)会被优先被清除掉,以腾出新的空间接受新的数据。

LFU(less frequently used)

最少使用策略,根据元素的被使用次数判断,清除使用次数较少的元素释放空间。

LRU(least recently used)

最近使用策略,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素释放空间。

其他

  1. 为缓存元素设置过期时间,清理超过过时时间的元素
  2. 随机清理
  3. 优先清理大对象

缓存简单分类

本地缓存:指的是在应用中的缓存组件,其最大的优点是应用和cache是在同一个进程内部,请求缓存非常快速,没有过多的网络开销等,在单应用不需要集群支持或者集群情况下各节点无需互相通知的场景下使用本地缓存较合适;同时,它的缺点也是因为缓存跟应用程序耦合,多个应用程序无法直接的共享缓存,各应用或集群的各节点都需要维护自己的单独缓存,对内存是一种浪费。

分布式缓存:指的是与应用分离的缓存组件或服务,其最大的优点是自身就是一个独立的应用,与本地应用隔离,多个应用可直接的共享缓存。缺点是:优点也就是缺点,因为自身是一个独立的应用,本地节点都需要与其进行通信,导致依赖网络,同时如果缓存服务崩溃可能会影响所有依赖节点

对于一些单个实例的服务,或者数据基本不会变化的数据都可以使用本地缓存来提高性能,反之可以使用分布式缓存

技术方案本身没有最好的,只有最合适的.

缓存的使用

Java集合类

在上面提供了一个简单的例子,DBMirror使用Map来实现一个简单的内存缓存,同时SetList都可以达到内存缓存的功能,根据并发情况可以选择不同的实现类,例如HashMapLinkedHashMapTreeMapLinkedTreeMapConcurrentHashMap… 总有一个满足你

这样实现很简单,但是也致命缺点:无法回收不常用的缓存

Guava Cache

说起 Guava, 很多人都不会陌生,它是 Google 提供的一个非常好用的 Java 工具包。Guava Cache 是 Guava 中的一个本地缓存实现,基于LRU算法实现,并提供了多种缓存过期策略,过期时间、容量等. 简化了缓存的使用,方便我们更加大胆的使用缓存

Caffeine

Caffeine是一个基于 Java8 开发的提供了近乎最佳命中率的高性能的缓存库。

在本地缓存方面,SpringFramework5.0(SpringBoot2.0)放弃了Google的GuavaCache,选择了「Caffeine」(Drop Guava caching - superseded by Caffeine [SPR-13797] #18370)。足以见证其在性能和可靠性上的优势.

其性能测试可以查看 https://github.com/ben-manes/caffeine/wiki/Benchmarks

Ehcache

Ehcache是纯Java开源缓存框架,配置简单、结构清晰、功能强大,是一个非常轻量级的缓存实现,我们常用的Hibernate里面就集成了相关缓存功能。

在早期开发的时候也用过这个,现在不知道是否还在使用

Memcached

一个高性能的、分布式的基于内存的key-value对象存储系统,用来存储小块的任意数据(字符串、对象)

通过访问其来较少数据库的读写压力

Redis

Redis 同样是一个高性能的基于内存中数据结构存储,用作数据库,缓存和消息代理。

它支持更多的数据结构,例如 strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes。

Redis具有内置的复制,Lua脚本,LRU逐出,事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis Cluster自动分区提供高可用性

Spring Cache

Spring Cache 并不是缓存的实现,而是一个缓存管理的抽象解决方案,这种方案消除了样板方法的使用,屏蔽了缓存的使用细节,而这是 Spring 最擅长干的.

Spring 的缓存技术还具备相当的灵活性,可以使用 SpEL 来定义缓存的 key 和各种 condition,提供了灵活的开箱即用的解决方案.

注意事项

在使用缓存的过程中,我们还要注意缓存不一致、缓存穿透、缓存击穿与缓存雪崩等问题,每种问题都是不小的问题

这篇写的并不长,每种都是简单介绍了一下,马上分几篇分别介绍一下各自的具体使用方法,敬请期待

参考

  1. spring cache
  2. https://github.com/google/guava/wiki/CachesExplained
  3. https://github.com/ben-manes/caffeine
  4. https://www.memcached.org/
  5. https://redis.io/

本文分享自微信公众号 - 双鬼带单(gh_773193545262),作者:张瑀楠

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-10-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JPA @Query实现,动态代理,注解, 正则,Spring扩展的使用

    上一篇文章中提到了如何使用注解完成一个简单的ORM,其中注解使用 JavaPersistenceAPI 但是其中没有我们需要的 @Query 和 @Param ...

    双鬼带单
  • 天才第一步 Docker 纸尿裤

    Docker Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到...

    双鬼带单
  • Spring Cloud:使用Ribbon实现负载均衡详解(下)

    自定义 Ribbon 负载均衡策略1. 继承`AbstractLoadBalancerRule`2. 重写 `choose` 方法使用针对某个服务使用针对全部应...

    双鬼带单
  • 聊聊Yii2和ThinkPHP5的文件缓存

    写PHP的老王
  • 亿级流量峰值,如何攻破?

    许多大型互联网系统,如电商、社交、新闻等App或网站,动辄日活千万甚至上亿,每分钟的峰值流量在数十万以上,架构上如何应对如此高的流量峰值呢?

    程序IT圈
  • 关于缓存和数据库强一致的可行方案

    我们在日常工作中经常会遇到要求缓存和数据库强一致性的问题,我们平常的做法是,确保数据库插入成功,然后再更新缓存,但有时候数据库插入成功后,缓存出现问题或者缓存系...

    小程故事多
  • 针对缓存的攻击和防御

    2.从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒

    ydymz
  • 使用缓存技术10年了,总结了如下经验!

    一位七牛的资深架构师曾经说过这样一句话:“Nginx+业务逻辑层+数据库+缓存层+消息队列,这种模型几乎能适配绝大部分的业务场景。

    范蠡
  • 缓存穿透、击穿、雪崩什么的傻傻分不清楚?

    对于缓存,大家肯定都不陌生,不管是前端还是服务端开发,缓存几乎都是必不可少的优化方式之一。在实际生产环境中,缓存的使用规范也是一直备受重视的,如果使用的不好,很...

    Java3y
  • 缓存与数据库一致性问题深度剖析

    当我们在做数据库与缓存数据同步时,究竟更新缓存,还是删除缓存,究竟是先操作数据库,还是先操作缓存?本文带大家深度分析数据库与缓存的双写问题,以供大家参考。

    黄泽杰

扫码关注云+社区

领取腾讯云代金券