我们编写的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:记录服务器的每次写操作,通过执行指令来恢复数据