SpringBoot 整合 Guava Cache 实现本地缓存

1、背景

【问】:项目中哪些地方会用到缓存?为什么要引入缓存?如何使用缓存?引入缓存后会带来哪些问题?

还得结合项目中的业务来回答。引入缓存,其实主要有两个用途:高性能、高并发

假设:某个操作非常频繁。比如:网站的商城首页 —— 需要频繁的从数据库里面获取商品数据,可能从数据库一顿各种操作下来,平均耗时 500 ms,随着请求频次越高,用户等待数据的返回结果时间越来越长,体验越来越差

如果此时,引入缓存,将数据库里面查询出来的商品数据信息,放入缓存服务里面,当用户再此发起查询操作的时候,直接从缓存服务里面获取,速度从耗时 500 ms,可能直接优化成 5 ms,体验上瞬间会上升好几个层次! —— 这就是引入缓存带来的高性能体验结果

引入缓存之前,以 mysql 数据库为例,单台机器一秒内的请求次数到达 2000 之后就会开始报警;引入缓存之后,以 redis 缓存服务器为例,单台机器一秒内的请求次数支持 110000 次,两者支持的并发量完全不是一个数量级的 —— 这就是引入缓存带来的高并发体验结果

尤其是对于流量很大的业务,引入缓存,给系统带来的提升是十分显著的

【问】:缓存和数据库为什么差距这么大?有什么区别?

在计算机中,数据的存储主要有两处:内存;磁盘

  • 内存:内存的数据读写性能 远超磁盘的读写性能;虽然读写性能非常高,但是当电脑重启之后,数据会全部清除
  • 磁盘:存入磁盘的数据,虽然读写性能很差,但是电脑重启之后数据不会丢失

因为两者的数据存储方案不同,造就了不同的实践用途

在项目中如何引入缓存呢?通常的做法如下:

  1. 当用户发起访问某数据的操作时,检查缓存服务里面是否存在,如果存在,直接返回;如果不存在,走数据库的查询服务
  2. 从数据库里面获取到有效数据之后,存入缓存服务,并返回给用户
  3. 当被访问的数据发生更新的时候,需要同时删除缓存服务,以便用户再次查询的时候,能获取到最新的数据

这对于简单的需要缓存的业务场景,能轻松应对。

但是面对复杂的业务场景和服务架构,尤其是对缓存要求比较高的业务,引入缓存的方式,也会跟着一起变化。

根据缓存面向的对象不同,缓存分为:本地缓存、分布式缓存和多级缓存

  • 本地缓存:在单个计算机服务实例中,直接把数据缓存到内存中进行使用

本地缓存是直接从本地内存中读取,没有网络开销。例如:秒杀系统或者数据量小的缓存等,比远程缓存更合适

  • 分布式缓存:现在的服务,大多都是以集群的方式来部署。即:同一个网站服务,同时在两台计算机里面部署,如用到的 session 会话,就无法同时共享,因此需要引入一个独立的缓存服务来连接两台服务器,这个独立部署的缓存服务就是分布式缓存
  • 多级缓存:在实际的业务中,本地缓存和分布式缓存会同时结合进行使用,当收到访问某个数据的操作时,会优先从本地缓存服务(也叫一级缓存)查询,如果没有,再从分布式缓存服务(也叫二级缓存)里面获取,如果也没有,最后再从数据库里面获取;从数据库查询完成之后,在依次更新分布式缓存服务、本地缓存服务

2、手写一个简单的本地缓存

使用缓存的时候,比较关注两个地方:

  1. 内存持久化
  2. 支持缓存的数据自动过期清除

对于简单的数据缓存,我们完全可以自行编写一套缓存服务,实现过程如下:

①:创建一个缓存实体类

@Data
public class CacheEntity {
   
   
    // 缓存键
    private String key;
    // 缓存键
    private Object value;
    // 过期时间
    private Long expireTime; 
}

②:缓存工具类

public class CacheUtil {
   
   

    // 缓存数据
    private final static Map<String, CacheEntity> CACHE_MAP = new ConcurrentHashMap<>();

    // 定时器线程池,用于清理过期缓存
    private static ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

    static {
   
   
        // 注册一个定时任务,服务启动 1000 毫秒后,每隔 500 毫秒执行一次
        Runnable task = CacheUtil::clear;
        executorService.scheduleAtFixedRate(task, 1000L, 500L, TimeUnit.MILLISECONDS);
    }
	
	// 添加缓存
    public static void put(String key, Object value) {
   
   
        put(key, value, 0L);
    }
	
	// 添加缓存
    public static void put(String key, Object value, Long expire) {
   
   
        CacheEntity cacheEntity = new CacheEntity();
        cacheEntity.setKey(key);
        cacheEntity.setValue(value);
        if (expire > 0) {
   
   
        	// 计算过期时间
            Long expireTime = System.currentTimeMillis() + Duration.ofSeconds(expire).toMillis();
            cacheEntity.setExpireTime(expireTime);
        }
        CACHE_MAP.put(key, cacheEntity);
    }
	
	// 获取
    public static Object get(String key) {
   
   
        if (CACHE_MAP.containsKey(key)) {
   
   
            return CACHE_MAP.get(key);
        }
        return null;
    }
	
	// 删除
    public static void remove(String key) {
   
   
        CACHE_MAP.remove(key);
    }
	
	// 清除过期缓存
    public static void clear() {
   
   
        if (CACHE_MAP.isEmpty()) {
   
   
            return;
        }
        CACHE_MAP.entrySet().removeIf(entityEntry 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值