简单的理解,缓存就是将数据从读取较慢的介质上读取出来放到读取较快的介质上,如磁盘–>内存。平时我们会将数据存储到磁盘上,如:数据库。如果每次都从数据库里去读取,会因为磁盘本身的IO影响读取速度,所以就有了像redis这种的内存缓存。可以将数据读取出来放到内存里,这样当需要获取数据时,就能够直接从内存中拿到数据返回,能够很大程度的提高速度。但是一般redis是单独部署成集群,所以会有网络IO上的消耗,虽然与redis集群的链接已经有连接池这种工具,但是数据传输上也还是会有一定消耗。所以就有了应用内缓存,如:caffeine。当应用内缓存有符合条件的数据时,就可以直接使用,而不用通过网络到redis中去获取,这样就形成了两级缓存。应用内缓存叫做一级缓存,远程缓存(如redis)叫做二级缓存
<?xml version="1.0" encoding="UTF-8"?>
<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.xiaoming</groupId>
<artifactId>springboot-ehcache-redis</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
</parent>
<dependencies>
<!-- SpringBoot web 核心组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--开启 cache 缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- ehcache缓存 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
###端口号配置
server:
port: 8080
###数据库配置
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
test-while-idle: true
test-on-borrow: true
validation-query: SELECT 1 FROM DUAL
time-between-eviction-runs-millis: 300000
min-evictable-idle-time-millis: 1800000
# 缓存配置读取
cache:
type: ehcache
ehcache:
config: classpath:app1_ehcache.xml
redis:
#连接的是第0个库
database: 0
#ip地址
host: 127.0.0.1
#端口号
port: 6379
#密码
password: 123456
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
timeout: 10000
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<diskStore path="java.io.tmpdir/ehcache-rmi-4000" />
<!-- 多台机器配置 -->
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=manual,rmiUrls=//127.0.0.1:5000/userCache" />
<!-- 配置rmi集群模式,集群监听地址和端口号 -->
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
properties="hostName=127.0.0.1,port=4000,socketTimeoutMillis=120000" />
<!-- 多播方式配置 搜索某个网段上的缓存 timeToLive 0是限制在同一个服务器 1是限制在同一个子网 32是限制在同一个网站 64是限制在同一个region
128是限制在同一个大洲 255是不限制 <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=224.1.1.1, multicastGroupPort=40000,
timeToLive=32" /> -->
<!-- 默认缓存 -->
<defaultCache maxElementsInMemory="1000" eternal="true"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
diskPersistent="true" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
<!-- demo缓存 userCache -->
<cache name="userCache" maxElementsInMemory="1000" eternal="false"
timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
<!-- 用于在初始化缓存,以及自动设置 -->
<bootstrapCacheLoaderFactory
class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" />
</cache>
</ehcache>
@Component
public class EhCacheUtils {
@Autowired
private EhCacheCacheManager ehCacheCacheManager;
// 添加本地缓存 (相同的key 会直接覆盖)
public void put(String cacheName, String key, Object value) {
Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName);
Element element = new Element(key, value);
cache.put(element);
}
// 获取本地缓存
public Object get(String cacheName, String key) {
Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName);
Element element = cache.get(key);
return element == null ? null : element.getObjectValue();
}
public void remove(String cacheName, String key) {
Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName);
cache.remove(key);
}
}
package com.xiaoming.service;
import com.xiaoming.annotation.ExtRedisTranscation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* springboot 2.0 整合redis
*
* @author xiaoming
* @Date 2019/10/29
* @blame Android Team
*/
@Component
public class RedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
*
* @param key
* @param object
* @param time
*/
public void set(String key, Object object, Long time){
//使该方法能够支持多种数据类型存放
//如果存放 String 类型
if (object instanceof String){
String value = (String) object;
stringRedisTemplate.opsForValue().set(key, value);
}
//如果存放 set 类型
if (object instanceof Set){
Set<String> value = (Set) object;
for (String string : value) {
stringRedisTemplate.opsForSet().add(key, string);
}
}
//设置有效期
if (time != null) {
stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
}
}
public void setString(String key, Object object, Long time){
//开启事务权限
stringRedisTemplate.setEnableTransactionSupport(true);
//开启事务
stringRedisTemplate.multi();
try {
String value = (String) object;
stringRedisTemplate.opsForValue().set(key, value);
} catch (Exception e) {
//事务回滚
stringRedisTemplate.discard();
} finally {
//提交事务
stringRedisTemplate.exec();
}
}
public void setSet(String key, Object object, Long time){
Set<String> value = (Set) object;
for (String string : value) {
stringRedisTemplate.opsForSet().add(key, string);
}
}
public String getString(String key){
return stringRedisTemplate.opsForValue().get(key);
}
}
@Service
public class UserService {
@Autowired
private EhCacheUtils ehCacheUtils;
private static final String CACHENAME_USERCACHE = "userCache";
@Autowired
private RedisService redisService;
@Autowired
private UserMapper userMapper;
public Users getUser(Long id) {
String key = this.getClass().getName() + "_" + Thread.currentThread().getStackTrace()[1].getMethodName()
+ "-id:" + id;
//1.先查找一级缓存(本地缓存),如果本地缓存有数据直接返回
Users ehcacheUsers = (Users) ehCacheUtils.get(CACHENAME_USERCACHE, key);
if (ehcacheUsers != null) {
System.out.println("使用key:" + key + ",查询一级缓存 ehcache 获取到 ehcacheUsers:" + JSONObject.toJSONString(ehcacheUsers));
return ehcacheUsers;
}
//2.如果本地缓存没有数据,直接查询二级缓存
String redisUserJson = redisService.getString(key);
if (!StringUtils.isEmpty(redisUserJson)) {
//将 json 转成对象(如果二级缓存中有数据直接返回二级缓存,并且更新一级缓存)
Users redisUsers = JSON.parseObject(redisUserJson, Users.class);
//更新一级缓存
ehCacheUtils.put(CACHENAME_USERCACHE, key, redisUsers);
System.out.println("使用key:" + key + ",查询二级缓存 redis 获取到 redisUsers:" + JSONObject.toJSONString(redisUsers));
return redisUsers;
}
//3.如果二级缓存 Redis 中也没有数据,查询数据库
Users user = userMapper.getUser(id);
if (user == null) {
return null;
}
//更新一级缓存和二级缓存
String userJson = JSONObject.toJSONString(user);
ehCacheUtils.put(CACHENAME_USERCACHE, key, user);
redisService.setString(key, userJson, 60L);
System.out.println("使用key:" + key + ",查询数据库 获取到 user:" + userJson);
return user;
}
}
@RequestMapping("/getUser")
public Users getUser(Long id){
return userService.getUser(id);
}