Spring Boot 缓存
Spring Boot 使用缓存在系统访问量越来越大后,首先出现瓶颈的往往是数据库,而为了减少数据库的压力,我们可以选择如下方式优化(暂时不考虑优化数据库的硬件、索引等):读写分离:通过将读操作分流到从节点,避免主节点过大。分库分表:通过将读操作分摊到多个节点,避免单节点压力过大。缓存:相比数据库来说,缓存往往能够提供更快的读速度,从而减小数据库的压力。Spring Cache:Spring 3.
Spring Boot 使用缓存
- 在系统访问量越来越大后,首先出现瓶颈的往往是数据库,而为了减少数据库的压力,我们可以选择如下方式优化(暂时不考虑优化数据库的硬件、索引等):
- 读写分离:通过将读操作分流到从节点,避免主节点过大。
- 分库分表:通过将读操作分摊到多个节点,避免单节点压力过大。
- 缓存:相比数据库来说,缓存往往能够提供更快的读速度,从而减小数据库的压力。
- Spring Cache:Spring 3.1 引入了激动人心的基于注释的缓存技术,它本质上不是一个具体的缓存实现方案(如 EhCache 或 OsCache),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能达到缓存方法的返回对象的效果。Spring 的缓存技术还具备相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案。
- 其特点如下:
- 通过少量的配置 annotation 注释即可使得既有代码支持缓存。
- 支持开箱即用 Out-Of-The-Box,即不用安装和部署额外第三方组件即可使用缓存。
- 支持 Spring Expression Language,能使用对象的任何属性或方法来定义缓存的 key 或 condition。
- 支持 AspectJ ,并通过其实现任何方法的缓存支持。
- 支持自定义 key 和自定义缓存管理者,具有相当的灵活性和扩展性。
- 简单来说,就是我们可以像使用
@Transaction
声明式事务那样,使用 Spring Cache 提供的@Cacheable
等注解声明式缓存。而在实现原理上,也是基于 Spring AOP 拦截,实现缓存相关的操作。
- 其特点如下:
- 注解
@Cacheable
: 添加在方法上,缓存方法的执行结果。执行过程如下:首先判断方法执行结果的缓存,如果有,直接返回缓存结果。没有的话,执行方法,获得方法结果,根据缓存条件将结果缓存,最后,返回结果。- 常用属性:
cacheNames
: 缓存名;values
: 和 cacheNames 属性相同;key
: 缓存的 key,允许空;condition
: 基于方法入参,判断要缓存的条件,允许空;unless
:基于方法返回,判断不缓存的条件,允许空;如果空,不进行入参判断
- 常用属性:
@CachePut
:添加在方法上,缓存方法的执行结果。不同于 CacheNames,它缓存步骤如下:首先,执行方法获得结果,也就是说,无论是否有缓存都会执行方法,其他一样。常用属性同上。- 一般来说,使用方式如下:
Cacheable
:搭配读操作,实现缓存的被动写;@CachePut
:搭配写操作,实现缓存的主动写。
- 一般来说,使用方式如下:
@CacheEvict
:添加在方法上,删除缓存@CacheConfig
:添加在类上@Caching
:添加在方法上,可以组合@Cacheable
、@CachePut
和@CacheEvict
注解,不常用@EnableCaching
:标记开启 Spring Cache 功能,一定要添加,一般添加在启动类上。
- Spring Boot 集成
-
在 Spring Boot 里,提供了
spring-boot-starter-cache
库,实现缓存的自动化配置。 -
在 Java 中,常见的缓存工具和框架:本地缓存:Ehcache、Caffeine(Ehcache 的功能更丰富,Caffeine 的性能比 Guava 好);分布式缓存:Redis、Memcached、Tair(Redis 常用)。[区别]:[https://blog.csdn.net/weixin_43621315/article/details/124121327?spm=1001.2014.3001.5502]
-
那 Spring 怎么知道使用哪种缓存呢?还是在 Spring 的自动配置类里:CacheAutoConfiguration 类
-
可以看到加载顺序,最次是 SimpleCache 和 NoOpCache ,这是 Spring 的本地缓存。
-
Ehcache : 一个纯 Java 的进程内缓存框架,具有快速、精干的特点,是 Hibernate 中默认的 CacheProvider。主要的特性有:快速、简单、多种缓存策略、缓存数据有两级:内存和磁盘,因此无需担心容量问题、缓存数据会在虚拟机重启的过程中写入磁盘、可以通过 RMI、可插入 API 等方式进行分布式缓存等。
- 依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
- yaml
spring: # datasource 数据源配置内容 datasource: url: jdbc:mysql://127.0.0.1:3306/db1?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: # cache 缓存配置内容 cache: type: ehcache # mybatis-plus 配置内容 mybatis-plus: configuration: map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 global-config: db-config: id-type: auto # ID 主键自增 logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) mapper-locations: classpath*:mapper/*.xml type-aliases-package: com.gui.ehcachetest.dataobject # logging logging: level: # dao 开启 debug 模式 mybatis 输入 sql cn: iocoder: springboot: lab21: cache: mapper: debug
与yaml 同目录创建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"> <!-- users 缓存 --> <!-- name:缓存名 --> <!-- maxElementsInMemory:最大缓存 key 数量 --> <!-- timeToLiveSeconds:缓存过期时长,单位:秒 --> <!-- memoryStoreEvictionPolicy:缓存淘汰策略 --> <cache name="users" maxElementsInMemory="1000" timeToLiveSeconds="60" memoryStoreEvictionPolicy="LRU"/> <!-- 缓存淘汰策略 --> </ehcache>
com.gui.ehcachetest.dataobject下创建 DO
@TableName(value = "users") @Data @ToString public class UserDo { private Integer id; private String username; private String password; private Date createTime; @TableLogic private Integer deleted; } //com.gui.ehcachetest.mapper 下创建mapper @Repository @CacheConfig(cacheNames = "users") public interface UserMapper extends BaseMapper<UserDo> { @Cacheable(value = "users",key = "#id") UserDo selectById(Integer id); @CachePut(value = "users",key = "#userDo.id") default UserDo insert0(UserDo userDo) { this.insert(userDo); return userDo; } @CacheEvict(key = "#id") int deleteById(Integer id); }
最后在启动类加上
@EnableCaching
就行了。 -
Redis
- pom.xml
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.3.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <!-- 去掉 Lettuce 的依赖,因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 --> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <!-- 引入 Jedis 的依赖,使 Spring Boot 实现对 Jedis 的自动化配置--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <!-- 实现对 json 序列化工具--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.80</version> </dependency> <!----> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.2.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
yaml
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/db1?useSSL=false&useUnicode=true&characterEncoding=UTF-8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: admin111111 redis: host: 127.0.0.1 port: 6379 password: database: 0 timeout: 0 jedis: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: -1 cache: type: REDIS mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: id-type: AUTO logic-delete-value: 1 logic-not-delete-value: 0 mapper-locations: - classpath*:mapper/*.xml type-aliases-package: com.gui.dataredis.dataobject logging: level: com: gui: dataredis: mapper: debug
其他的与 Ehcache 一致。
直接测试:
@Resource private UserMapper userMapper; @Resource private CacheManager cacheManager; @Test public void testSelectById() { UserDO userDO = userMapper.selectById(1); if (userDO != null) { System.out.println(userDO); } // assert userDO != null; Assert.notNull(new IErrorCode() { @Override public long getCode() { return 0; } @Override public String getMsg() { return "缓存为空"; } }, Objects.requireNonNull(cacheManager.getCache("users")).get(userDO.getId(), UserDO.class)); userDO = userMapper.selectById(1); System.out.println(userDO); } @Test public void testInsertUser() { UserDO userDO = new UserDO(); userDO.setUsername(UUID.randomUUID().toString()); userDO.setPassword("cool"); userDO.setCreateTime(new Date()); userDO.setDeleted(0); userMapper.insert0(userDO); // Assert.notNull(new IErrorCode() { @Override public long getCode() { return 0; } @Override public String getMsg() { return "缓存为空"; } }, Objects.requireNonNull(cacheManager.getCache("users")).get(userDO.getId(), UserDO.class)); } @Test public void testDelete() { UserDO userDO = new UserDO(); userDO.setUsername(UUID.randomUUID().toString()); userDO.setPassword("cool"); userDO.setCreateTime(new Date()); userDO.setDeleted(0); userMapper.insert0(userDO); Assert.notNull(new IErrorCode() { @Override public long getCode() { return 0; } @Override public String getMsg() { return "缓存为空"; } }, Objects.requireNonNull(cacheManager.getCache("users")).get(userDO.getId(), UserDO.class)); userMapper.deleteById(userDO.getId()); Assert.isNull(new IErrorCode() { @Override public long getCode() { return 0; } @Override public String getMsg() { return "缓存bu为空"; } }, Objects.requireNonNull(cacheManager.getCache("users")).get(userDO.getId(), UserDO.class)); }
转载于:https://www.iocoder.cn/Spring-Boot/Cache/?github
更多推荐
所有评论(0)