SPRING缓存的应用

本文介绍Spring框架中的缓存管理机制,包括使用注解和XML配置的方式,并探讨如何利用ConcurrentMap实现缓存功能。此外,还讨论了使用Redis作为外部缓存时如何保证数据库与缓存的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    我们编写的spring中的组件一般是无状态的,这样扩展性更好。这样会引起相同的功能可能被一遍遍执行(如数据库中同一个参数的查询,相同的计算逻辑多次使用(可能还是远程调用)),但每次执行的结果是相同的。因此我们需要将结果记录,下次请求时直接返回,这就需要用到spring缓存。

    spring本身并没有提供缓存解决方案,但对缓存功能提供了声明式的支持(javaconfig、注解、xml配置),能够与多种缓存实现进行集成。下面分别用注解和xml配置应用ConcurrentMap缓存,写下demo用以备忘。

    spring中缓存的工作方式是 创建一个切面(aspect)并以spring缓存注解的方法为切入点(pointcut),或者通过xml指定切面的切入点及定义的缓存通知。根据缓存注解或者缓存通知的声明,来做对应操作(从缓存中获取数据,或将数据添加到缓存,或从缓存中移除数据)

工程pom依赖如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.glt</groupId>
  <artifactId>SpringExample</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <dependencies>
  <!-- Spring依赖  start -->
   <!-- 1.Spring核心依赖 -->
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-core</artifactId>
       <version>4.3.7.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-beans</artifactId>
       <version>4.3.7.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       <version>4.3.7.RELEASE</version>
   </dependency>
<!-- 2.Spring dao依赖 -->
<!-- spring-jdbc包括了一些如jdbcTemplate的工具类 -->
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-jdbc</artifactId>
       <version>4.3.7.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-tx</artifactId>
       <version>4.3.7.RELEASE</version>
   </dependency>
   <!-- 3.Spring web依赖 -->
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-web</artifactId>
       <version>4.3.7.RELEASE</version>
   </dependency>
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-webmvc</artifactId>
       <version>4.3.7.RELEASE</version>
   </dependency>
   <!-- 4.Spring test依赖:方便做单元测试和集成测试 -->
   <dependency>
       <groupId>org.springframework</groupId>
       <artifactId>spring-test</artifactId>
       <version>4.3.7.RELEASE</version>
   </dependency>
   <!-- Spring依赖 end  -->
   <!-- aop使用  start -->
   <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.2</version>
   </dependency>
   <dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.8.2</version>
</dependency>
<!-- aop使用 end  -->
  </dependencies>
</project>

一 注解

1. 有需缓存方法的bean:/SpringExample/src/main/java/com/glt/annotation/demo/service/user/UserService.java

package com.glt.annotation.demo.service.user;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Cacheable(value="cache",key="1")
public String GetDemoUserUseCache(int id){
System.out.println(id+"GetDemoUserUseCache");
return "GetDemoUserUseCache";
}

}

2. xml配置文件:/SpringExample/src/main/resources/META-INF/spring/application-cache-annotation.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" 
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:dcits="http://code.alibabatech.com/schema/dubbo"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://code.alibabatech.com/schema/dubbo 
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd
        http://www.springframework.org/schema/cache
      http://www.springframework.org/schema/cache/spring-cache.xsd">
    <context:component-scan base-package="com.glt.annotation" >
        <context:include-filter type="annotation"
expression="org.springframework.stereotype.Component" />
    </context:component-scan>
    <cache:annotation-driven />
    <bean id="cacheManager" 
      class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" />
</beans>

二 xml配置

1. 有需缓存方法的bean:/SpringExample/src/main/java/com/glt/demo/service/user/UserService.java

package com.glt.demo.service.user;
public class UserService {
    String GetDemoUserUseCache(int id){
System.out.println(id+"GetDemoUserUseCache");
return "GetDemoUserUseCache";
}

}

2.xml配置文件:/SpringExample/src/main/resources/META-INF/spring/application-cache.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:cache="http://www.springframework.org/schema/cache"
   xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
     http://www.springframework.org/schema/cache
     http://www.springframework.org/schema/cache/spring-cache.xsd">
    <bean id="cacheManager" 
      class="org.springframework.cache.concurrent.ConcurrentMapCacheManager" />
<cache:advice id="cacheAdvice" >
<cache:caching>
<!-- <cache:cacheable cache="cache" method="GetDemoUserUseCache" key="0" /> -->
<cache:cacheable cache="cache" method="GetDemoUserUseCache" />
</cache:caching>
</cache:advice>
<aop:config>
<aop:advisor advice-ref="cacheAdvice" 
pointcut="execution(* com.glt.demo.service.user.UserService.*(..))" />
</aop:config>
<bean id="userService" class="com.glt.demo.service.user.UserService" />

</beans>

共用测试类:/SpringExample/src/main/java/com/glt/main/AppMain.java

package com.glt.main;

import java.util.Collection;
import java.util.Iterator;
import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.glt.annotation.demo.service.user.UserService;

public class AppMain {
private final static String SPRING_CONFIG_PATH1 = "META-INF/spring/application.xml"; //配置文件实现aop
private final static String SPRING_CONFIG_PATH2 = "META-INF/spring/application-annotation.xml"; //基于注解实现aop
private final static String SPRING_CONFIG_PATH3 = "META-INF/spring/application-cache.xml"; //基于xml实现缓存
private final static String SPRING_CONFIG_PATH4 = "META-INF/spring/application-cache-annotation.xml"; //基于注解实现缓存
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext(SPRING_CONFIG_PATH4);
UserService userService = (UserService)context.getBean("userService");
// userService.GetDemoUser(1);
userService.GetDemoUserUseCache(0);
userService.GetDemoUserUseCache(0);
userService.GetDemoUserUseCache(1);
userService.GetDemoUserUseCache(2);
CacheManager cacheManager = (CacheManager)context.getBean("cacheManager");
Collection<String> cns = cacheManager.getCacheNames();
Iterator<String> i = cns.iterator();
int n = 0;
while(i.hasNext()){
String name = i.next();
System.out.println(++n +"-cache:\tkey="+name+"\tvalue="+cacheManager.getCache(name));
}
}
}

redis用作缓存如何保证数据库与缓存的一致性

* 总体操作:

1. 首次读取数据时,先读取数据库,加载到redis中

2. 数据更新时,先更新数据库,然后删除redis中的缓存

* 特殊场景说明:

1. 两个并发的线程,a线程读取数据库刷新redis前,b线程更新了数据库,若不做特殊处理,会导致a线程将过期的数据写入redis

两种方案:①悲观锁,加载数据到redis的过程不允许并发执行。对更新数据库+清除redis的操作,和查数据库+写入redis的操作,加分布式锁,使得二者的操作互斥;②乐观锁,redis中对每个key同时存一个对应key的版本号(单向递增),读redis时返回数据和版本号;数据库更新后,清除redis值,同时递增版本号;写redis时检测版本号和redis里的最新版本号是否一致,不一致,则拒绝写入,等待下次查询触发写入

2. 缓存穿透: 查询的数据不存在,不会缓存到redis,导致每次请求都命中到数据库;解决方案:数据库不存在的记录,也缓存特殊值,避免穿透缓存

3. 缓存击穿: 某个缓存失效时,如果有大量并发请求该缓存,则请求将全部击中数据库,导致数据库瞬时压力过大。解决方案:从数据库加载到缓存的过程使用锁,避免大量并发访问到数据库

4. 缓存雪崩: 大量缓存在同一个时间失效,导致大量请求都命中到数据库。解决方案:不同的缓存设置不同的过期时间。

redis 多线线程 模型:https://blog.csdn.net/ldw201510803006/article/details/124790121

低版本单线程、

4以后版本增加后台线程,用于处理耗时任务,如bgsave、大对象释放等

6以后版本呢增加多线程(用于处理网络io) ,主线程接收io任务然后分配给io线程进行io读写等操作,命令处理模块依然采用单线程模型

# redis两种持久化方案

参考文章:redis两种持久化方式的区别_liuyukuan的博客-CSDN博客

* RDB:指定时间间隔进行快照存储,单独fork一个子进程来进行持久化。配置RDB持久化的间隔时间,save 900 1    --900秒内发生一个key的变化,则进行持久化

* AOF:记录服务器的每次写操作,通过执行指令来恢复数据

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值