当前位置:   article > 正文

SpringBoot 缓存_springboot本地缓存

springboot本地缓存


 

缓存概述

查询频率高、更新频率低的数据可以加缓存,缓存可分为本地缓存、分布式缓存2大类。
 l

本地缓存

  • 比分布式缓存速度快、效率高;
  • 占用应用服务器内存,缓存的数据量通常不能太大;
  • 适合单机应用中使用,分布式环境下容易出现数据不一致问题,如果要在分布式环境中使用本地、远程二级缓存,本地缓存的有效期应该设置很短,建议5s以内,减少本地缓存脏数据问题;
  • 常见的比如guava、ehcache、caffeine;
     

本地缓存常见实现

  • 自己用 ConcurrentLinkedHashMap 实现;
  • guava:基于 CurrentMap 实现;
  • ehcache:纯java编写的进程内缓存框架,快速精干,缓存数据存储有内存、硬盘两级,无需担心容量问题;
  • caffeine:基于java8,在内存中缓存数据,性能极高、高命中率、低内存占用,在spring 2.x中取代guava成为默认的本地缓存组件

 

分布式缓存

  • 常用于多个服务、应用之间的数据共享,适合分布式环境中使用
  • 通常使用专用服务器,支持缓存大量数据;
  • 通常使用内存缓存数据,性能高
     

分布式缓存常见实现

  • memcached:只支持String类型;缺乏认证、安全管制机制,使用时需要将memcached服务器放在防火墙后;不支持持久化,宕机全部数据丢失,一般只用于缓存数据;支持多核cpu;
  • redis:数据类型丰富;支持数据持久化,可保存数据到硬盘,容灾能力强;功能丰富,支持发布/订阅、队列等多种功能;redis 6.0前使用单核cpu,存储大量数据时memcached整体性能高于redis。

 

springboot 的缓存接口

springboot 定义了一套缓存标准,提供了默认实现,也可以引入第三方的缓存实现。

基本配置

1、pom.xml
创建项目时勾选 I/O -> Spring cache abstraction,或自行添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

2、启动类加 @EnableCaching
 

配置缓存管理器

可以在 yml 中配置,也可以用 java 代码配置

spring:
  cache:
    #缓存类型,参见CacheType,缺省时默认为 SIMPLE
    type: SIMPLE
    #缓存名称,list,需要在此处列出所有用到的缓存名称
    cache-names: userInfo,systemConfig
    # 一些缓存类型可指定扩展配置,参见 CacheProperties 中对应的静态类的属性
#    caffeine:
#      spec: initialCapacity=100,maximumSize=1000,expireAfterAccess=1800s
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

常用的缓存类型:CAFFEINE、EHCACHE、REDIS、SIMPLE(基于ConcurrentMap实现,无需引入第三方缓存实现框架)、NONE(不使用缓存)。

org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration 源码如下

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CacheManager.class)  //未配置缓存管理器时默认使用 Simple
@Conditional(CacheCondition.class)
class SimpleCacheConfiguration {

	@Bean
	ConcurrentMapCacheManager cacheManager(CacheProperties cacheProperties,
			CacheManagerCustomizers cacheManagerCustomizers) {
		ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
		List<String> cacheNames = cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			cacheManager.setCacheNames(cacheNames);
		}
		return cacheManagerCustomizers.customize(cacheManager);
	}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

 

使用缓存

方式1 缓存注解

@Service
public class UserService {

	@Resource
	private UserDao userDao;
	
    @Cacheable(cacheNames = "userInfo", key = "#id")
    public User getUserById(Long id) {
        return userDao.getUserById(id);
    }

    @CacheEvict(cacheNames = "userInfo", key = "#user.id")
    public void updateUserInfo(User user) {
		userDao.updateUserInfo(user);
    }

    @CacheEvict(cacheNames = "userInfo", key = "#id")
    public void deleteById(Long id) {
		userDao.deleteById(id);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

缓存注解

  • @Cacheable:和查询搭配使用,先查本地缓存,本地缓存中有就直接返回,没有才执行方法体中的代码进行查询,执行成功后将返回值写入本地缓存;
  • @CacheEvict:方法执行成功后删除指定的本地缓存;
  • @CachePut:方法执行成功后将返回值写入缓存,已存在则覆盖。

以上注解都是在目标方法执行完毕后才写入、删除缓存,如果方法中抛出异常,则不写入、删除缓存。

 

缓存注解的属性

  • cacheNames:指定 cacheName,数组,可以指定多个
  • key:指定缓存key
  • condition:指定注解生效的条件,满足条件时才会执行对应的缓存操作,未指定时默认满足条件
  • unless:@Cacheable 特有的属性,指定方法执行成功后不写入缓存的条件,未指定时默认写入缓存。condition 是在方法执行前校验条件,获取不到 #result,unless 是方法执行成功后校验条件,可以获取到 #result。
     

key、condition、unless 都可以使用SpEL取值,可以使用内置对象,常见的内置对象如下

  • #root.methodName:当前方法名
  • #root.method.name:当前方法
  • #root.target:当前方法所属的对象
  • #root.targetClass:当前方法所属的对象的Class对象
  • #root.args:实参表, #root.args[0]表示第一个参数
  • #result:方法返回值,只能在@Cacheable的unless属性中使用

 

方式2 调用方法

CacheManager 提供了一系列操作缓存的方法,此处可以自行封装为缓存工具类,示例

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;

/**
 * 统一缓存服务
 */
@Slf4j
@Service
public class CommonCacheService {

    @Resource
    private CacheManager cacheManager;

    /**
     * 清除指定的key,可能会以异步之类的方式延迟执行
     *
     * @param cacheName 缓存名称
     * @param cacheKey  缓存key
     */
    public void delayClear(String cacheName, String cacheKey) {
        Assert.isTrue(StringUtils.isNotBlank(cacheKey), "cacheKey不能为空");
        Cache cache = this.getCacheNotNull(cacheName);
        cache.evict(cacheKey);
    }

    /**
     * 清除指定的key,立即执行
     *
     * @param cacheName 缓存名称
     * @param cacheKey  缓存key
     */
    public void clear(String cacheName, String cacheKey) {
        Assert.isTrue(StringUtils.isNotBlank(cacheKey), "cacheKey不能为空");
        Cache cache = this.getCacheNotNull(cacheName);
        cache.evictIfPresent(cacheKey);
    }

    /**
     * 清除缓存名称下所有的key,可能会以异步之类的方式延迟执行
     *
     * @param cacheName 缓存名称
     */
    public void delayClearAll(String cacheName) {
        Cache cache = this.getCacheNotNull(cacheName);
        cache.clear();
    }

    /**
     * 清除缓存名称下所有的key,立即执行
     *
     * @param cacheName 缓存名称
     */
    public void clearAll(String cacheName) {
        Cache cache = this.getCacheNotNull(cacheName);
        cache.invalidate();
    }

    /**
     * 设置或更新value,可能会以异步之类的方式延迟执行
     *
     * @param cacheName 缓存名称
     * @param cacheKey  缓存key
     * @param value     值
     */
    public void delayPut(String cacheName, String cacheKey, Object value) {
        Assert.isTrue(StringUtils.isNotBlank(cacheKey), "cacheKey不能为空");
        Cache cache = this.getCacheNotNull(cacheName);
        cache.put(cacheKey, value);
    }

    /**
     * 设置或更新value,立即执行
     *
     * @param cacheName 缓存名称
     * @param cacheKey  缓存key
     * @param value     值
     */
    public void put(String cacheName, String cacheKey, Object value) {
        Assert.isTrue(StringUtils.isNotBlank(cacheKey), "cacheKey不能为空");
        Cache cache = this.getCacheNotNull(cacheName);
        cache.putIfAbsent(cacheKey, value);
    }

    /**
     * 获取指定key对应的value
     *
     * @param cacheName 缓存名称
     * @param cacheKey  缓存key
     * @param clazz     目标类型的class对象
     * @param <T>       目标类型
     * @return T
     */
    @Nullable
    public <T> T get(String cacheName, String cacheKey, Class<T> clazz) {
        Assert.isTrue(StringUtils.isNotBlank(cacheKey), "cacheKey不能为空");
        Cache cache = this.getCacheNotNull(cacheName);
        return cache.get(cacheKey, clazz);
    }

    /**
     * 获取指定key对应的value(String类型)
     *
     * @param cacheName 缓存名称
     * @param cacheKey  缓存key
     * @return String
     */
    @Nullable
    public String get(String cacheName, String cacheKey) {
        return this.get(cacheName, cacheKey, String.class);
    }

    private Cache getCacheNotNull(String cacheName) {
        Assert.isTrue(StringUtils.isNotBlank(cacheName), "cacheName不能为空");
        Cache cache = cacheManager.getCache(cacheName);
        Assert.notNull(cache, "指定的cache不存在,cacheName=" + cacheName);
        return cache;
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126

 

整合缓存实现组件

caffeine

1、pom.xml

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

 

2、缓存配置
方式一 yml

spring:
  cache:
    type: caffeine
    cache-names: userInfo,systemConfig
    caffeine:
      spec: initialCapacity=100,maximumSize=1000,expireAfterAccess=5s
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

spec是针对所有缓存的设置,不能针对各个缓存分别进行配置,比较死板。

3种缓存淘汰策略

  • expireAfterAccess:基于最后访问时间
  • expireAfterWrite:基于最后写入时间
  • expireAfter:自定义的缓存淘汰策略

 

方式二 配置类(推荐)

import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCache userInfoCache = this.buildCache("userInfo", 60, 100, 10000);
        CaffeineCache systemConfigCache = this.buildCache("systemConfig", 30, 10, 1000);
        SimpleCacheManager manager = new SimpleCacheManager();
        manager.setCaches(Arrays.asList(userInfoCache, systemConfigCache));
        return manager;
    }

    private CaffeineCache buildCache(String cacheName, int expireSecondsAfterWrite, int initialCapacity, long maximumSize) {
        return new CaffeineCache(cacheName, Caffeine.newBuilder()
                .expireAfterWrite(expireSecondsAfterWrite, TimeUnit.SECONDS)
                .initialCapacity(initialCapacity)
                .maximumSize(maximumSize)
                .build());
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

 

ehcache

1、pom.xml

<!-- ehcache3.x -->
<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.9.0</version>
</dependency>

<!-- ehcahce 2.x,已不再维护 -->
<!--<dependency>-->
<!--    <groupId>net.sf.ehcache</groupId>-->
<!--    <artifactId>ehcache</artifactId>-->
<!--    <version>2.10.6</version>-->
<!--</dependency>-->
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

 

2、resources下新建ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

    <!-- 磁盘缓存位置。java.io.tmpdir是系统默认的临时文件目录,也可以使用自定义的路径 -->
    <diskStore path="java.io.tmpdir"/>

    <!-- 默认缓存管理器 -->
    <!-- memoryStoreEvictionPolicy缓存淘汰策略,支持FIFO、LRU、LFU -->
    <!-- maxElementsInMemory内存中的元素最大数量,overflowToDisk内存元素数量达到最大值时是否存储到硬盘 -->
    <!-- timeToLiveSeconds缓存有效期,timeToIdleSeconds缓存有效期内多久未访问就自动清除 -->
    <defaultCache memoryStoreEvictionPolicy="LRU"
                  maxElementsInMemory="1000"
                  overflowToDisk="false"
                  timeToLiveSeconds="10"
                  timeToIdleSeconds="5"/>

    <!-- 自定义缓存管理器 -->
    <cache name="userInfo"
           memoryStoreEvictionPolicy="LRU"
           maxElementsInMemory="1000"
           overflowToDisk="false"
           timeToLiveSeconds="10"
           timeToIdleSeconds="5"/>

    <cache name="systemConfig"
           memoryStoreEvictionPolicy="LRU"
           maxElementsInMemory="1000"
           overflowToDisk="false"
           timeToLiveSeconds="10"
           timeToIdleSeconds="5"/>

</ehcache>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

这是ehcache2.x的配置方式,3.x向下兼容2.x的配置方式
 

3、yml

spring:
    cache:
    	#指定ehcache配置文件位置
        jcache:
            config: classpath:ehcache.xml
#        ehcache:
#            config: classpath:ehcache.xml
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.x用jcache.config,2.x使用ehcache.config。3.x向下兼容2.x的配置方式,也可以使用ehcache.config。

 

redis

1、pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 1
  • 2
  • 3
  • 4

 

2、yml

spring:
  #redis配置
  redis:
    host: 192.168.1.1
    #集群则使用 cluster.nodes: 192.168.1.1:6379,192.168.1.2:6379,192.168.1.3:6379
    port: 6379
    database: 0
    timeout: 3000
  #缓存配置
  cache:
    type: REDIS
    cache-names: userInfo,systemConfig
    redis:
      #是否使用key前缀
      use-key-prefix: true
      key-prefix: mall_
      #是否缓存null值
      cache-null-values: false
      #缓存有效期
      time-to-live: 300s
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小惠珠哦/article/detail/750187
推荐阅读
相关标签
  

闽ICP备14008679号