缓存
缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质。
使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能。
缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间。
缓存使用
1.导入缓存技术对应的starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.启用缓存
@SpringBootApplication
//开启缓存功能
@EnableCaching
public class Springboot19CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot19CacheApplication.class, args);
}
}
3.设置当前操作的结果数据进入缓存
@Cacheable(value="cacheSpace",key="#id")
public Book getById(Integer id) {
return bookDao.selectById(id);
}
SpringBoot提供的缓存技术除了提供默认的缓存方案,还可以对其他缓存技术进行整合,统一接口,方便缓存技术的开发与管理:Generic、JCache、Hazelcast、Infinispan、Couchbase、Caffeine、Ehcache、Redis、memcached、simple(默认)。
缓存使用案例
需求:
输入手机号获取验证码,组织文档以短信形式发送给用户(页面模拟)。
输入手机号和验证码验证结果。
需求分析:
提供controller,传入手机号,业务层通过手机号计算出独有的6位验证码数据,存入缓存后返回此数据。
提供controller,传入手机号与验证码,业务层通过手机号从缓存中读取验证码与输入验证码进行比对,返回比对结果。
缓存供应商变更:Ehcache
1.加入Ehcache坐标
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
2.缓存设置为使用Ehcache
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
password: root
cache:
type: ehcache
ehcache:
config: ehcache.xml
3.提供ehcache配置文件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"
updateCheck="false">
<diskStore path="D:\ehcache" />
<!--默认缓存策略 -->
<!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
<!-- diskPersistent:是否启用磁盘持久化-->
<!-- maxElementsInMemory:最大缓存数量-->
<!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
<!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果,可用于记录时效性数据,例如验证码-->
<!-- timeToLiveSeconds:最大存活时间-->
<!-- memoryStoreEvictionPolicy:缓存清除策略-->
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU" />
<cache
name="smsCode" //通过name去管理不同的cache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="10"
timeToLiveSeconds="10"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
4.业务层方法
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Override
@CachePut(value = "smsCode",key="#tele")
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
//取出内存中的验证码与传递过来的验证码比对,如果相同,返回true
String code = smsCode.getCode();
String cacheCode = codeUtils.get(smsCode.getTele());
return code.equals(cacheCode);
}
5.CodeUtils
@Component
public class CodeUtils {
private String [] patch = {"000000","00000","0000","000","00","0",""};
public String generator(String tele){
int hash = tele.hashCode();
int encryption = 20206666;
long result = hash ^ encryption;
long nowTime = System.currentTimeMillis();
result = result ^ nowTime;
long code = result % 1000000;
code = code < 0 ? -code : code;
String codeStr = code + "";
int len = codeStr.length();
return patch[len] + codeStr;
}
@Cacheable(value = "smsCode",key="#tele")
public String get(String tele){
return null;
}
// public static void main(String[] args) {
// System.out.println(new CodeUtils().generator("18866668888"));
// }
}
缓存供应商变更:Redis
1.加入Redis坐标(缓存供应商实现)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.配置Redis服务器,缓存设定为使用Redis
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username: root
password: root
cache:
type: redis
redis:
host: localhost
port: 6379
3.设置Redis相关配置
cache:
type: redis
redis:
cache-null-values: true # 是否允许存储空值
time-to-live: 10s # 有效时长
use-key-prefix: false # 是否使用前缀名(系统定义前缀名)
key-prefix: # 追加自定义前缀
redis:
host: localhost
port: 6379
缓存供应商变更:memcached
安装memcached
管理员命令行下运行
memcached.exe -d isntall
运行memcached
1、启动服务
memcached.exe -d start
2、停止服务
memcached.exe -d stop
1.加入Xmemcached坐标
<dependency>
<groupId>com.googlecode.xmemcached</groupId>
<artifactId>xmemcached</artifactId>
<version>2.4.7</version>
</dependency>
2.配置Xmemcached服务器必要属性
memcached:
# memcached服务器地址
servers: localhost:11211
# 连接池的数量
poolSize: 10
# 设置默认操作超时
opTimeout: 3000
3.创建读取属性配置信息类,加载配置
@Component
@ConfigurationProperties(prefix = "memcached")
@Data
public class XMemcachedProperties {
private String servers;
private int poolSize;
private long opTimeout;
}
4.创建客户端配置类
@Configuration
public class XMemcachedConfig {
@Autowired
private XMemcachedProperties memcachedProperties;
@Bean
public MemcachedClient getMemcachedClient() throws IOException {
MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder(memcachedProperties.getServers());
memcachedClientBuilder.setConnectionPoolSize(memcachedProperties.getPoolSize());
memcachedClientBuilder.setOpTimeout(memcachedProperties.getOpTimeout());
MemcachedClient memcachedClient = memcachedClientBuilder.build();
return memcachedClient;
}
}
5.配置memcached属性
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
//以下是springboot中使用xmemcached
@Autowired
private MemcachedClient memcachedClient;
@Override
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
try {
memcachedClient.set(tele,10,code); //
} catch (Exception e) {
e.printStackTrace();
}
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
String code = null;
try {
code = memcachedClient.get(smsCode.getTele()).toString();
} catch (Exception e) {
e.printStackTrace();
}
return smsCode.getCode().equals(code);
}
}
缓存供应商变更:jetcache(阿里)
jetCache对SpringCache进行了封装,在原有功能基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能。
jetCache设定了本地缓存与远程缓存的多级缓存解决方案:
本地缓存(local):LinkedHashMap、Caffeine。
远程缓存(remote):Redis、Tair。
远程缓存(remote):Redis;本地缓存(local):linkedhashmap
1.加入jetCache坐标
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.6.2</version>
</dependency>
2.配置远程缓存必要的属性
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: linkedhashmap
keyConvertor: fastjson
limit: 100
remote:
default: //默认的远程缓存
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
sms: //自己定义的远程缓存的名字
type: redis
host: localhost
port: 6379
poolConfig:
maxTotal: 50
3.开启jetCache注解支持
@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
public class Springboot20JetcacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetcacheApplication.class, args);
}
}
4.操作
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
//声明缓存对象
@CreateCache(name="jetCache",expire = 3600)
private Cache<String,String> jetCache;
@Override
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
jetCache.put(tele,code);
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
//取出内存中的验证码与传递过来的验证码比对,如果相同,返回true
String code = jetCache.get(smsCode.getTele());
return smsCode.getCode().equals(code);
}
}
使用方法注解操作缓存
1.开启方法注解
@SpringBootApplication
//jetcache启用缓存的主开关
@EnableCreateCacheAnnotation
//开启方法注解缓存,必须和@EnableCreateCacheAnnotation搭配使用
@EnableMethodCache(basePackages = "com.itheima")
public class Springboot20JetcacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot20JetcacheApplication.class, args);
}
}
2.使用方法注解操作缓存
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
@Cached(name = "book_",expire = 3600,key = "#id",cacheType = CacheType.REMOTE)
@CacheRefresh(refresh = 5)//5分钟刷新一次缓存,因为删除数据之后再查询就只能从数据库中查询而不是走缓存
public Book getById(Integer id) {
return bookDao.selectById(id);
}
@Override
public boolean save(Book book) {
return bookDao.insert(book) > 0;
}
@Override
@CacheUpdate(name = "book_",key = "#book.id",value = "#book")
public boolean update(Book book) {
return bookDao.updateById(book) > 0;
}
@Override
@CacheInvalidate(name = "book_",key = "#id")//数据库缓存同步删除
public boolean delete(Integer id) {
return bookDao.deleteById(id) > 0;
}
@Override
public List<Book> getAll() {
return bookDao.selectList(null);
}
}
3.缓存对象必须保障可序列化
@Data
public class Book implements Serializable {
}
jetcache:
statIntervalMinutes: 1
local:
default:
type: linkedhashmap
keyConvertor: fastjson
valueEncode: java //序列化转为Java对象
valueDecode: java
limit: 100
remote:
default:
type: redis
host: localhost
port: 6379
valueEncode: java
valueDecode: java
poolConfig:
maxTotal: 50
4.查看缓存统计报告
jetcache:
statIntervalMinutes: 1
缓存供应商变更:j2cache
j2cache是一个缓存整合框架,可以提供缓存的整合方案,使各种缓存搭配使用,自身不提供缓存功能。
基于 ehcache + redis 进行整合。
1.加入j2cache坐标,加入整合缓存的坐标
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-core</artifactId>
<version>2.8.4-release</version>
</dependency>
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-spring-boot2-starter</artifactId>
<version>2.8.0-release</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
2.配置使用j2cache(application.yml)
server:
port: 80
j2cache:
config-location: j2cache.properties
3.配置一级缓存与二级缓存以及一级缓存数据到二级缓存的发送方式
(j2cache.properties)
#一级缓存的配置
j2cache.L1.provider_class = ehcache
ehcache.configXml = ehcache.xml
#二级缓存的配置
j2cache.L2.provider_class =net.oschina.j2cache.cache.support.redis.SpringRedisProvider
j2cache.L2.config_section = redis
redis.hosts = localhost:6379
#一级缓存的数据如何到达二级缓存
j2cache.broadcast =net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
4.配置ehcache.xml(和使用ehcache缓存一样的配置)
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="D:\ehcache" />
<!--默认缓存策略 -->
<!-- external:是否永久存在,设置为true则不会被清除,此时与timeout冲突,通常设置为false-->
<!-- diskPersistent:是否启用磁盘持久化-->
<!-- maxElementsInMemory:最大缓存数量-->
<!-- overflowToDisk:超过最大缓存数量是否持久化到磁盘-->
<!-- timeToIdleSeconds:最大不活动间隔,设置过长缓存容易溢出,设置过短无效果,可用于记录时效性数据,例如验证码-->
<!-- timeToLiveSeconds:最大存活时间-->
<!-- memoryStoreEvictionPolicy:缓存清除策略-->
<defaultCache
eternal="false"
diskPersistent="false"
maxElementsInMemory="1000"
overflowToDisk="false"
timeToIdleSeconds="60"
timeToLiveSeconds="60"
memoryStoreEvictionPolicy="LRU" />
</ehcache>
5.使用缓存
@Service
public class SMSCodeServiceImpl implements SMSCodeService {
@Autowired
private CodeUtils codeUtils;
@Autowired
private CacheChannel cacheChannel;
@Override
public String sendCodeToSMS(String tele) {
String code = codeUtils.generator(tele);
cacheChannel.set("sms",tele,code);
return code;
}
@Override
public boolean checkCode(SMSCode smsCode) {
//取出内存中的验证码与传递过来的验证码比对,如果相同,返回true
String code = cacheChannel.get("sms",smsCode.getTele()).asString();
return smsCode.getCode().equals(code);
}
}