目录
应用背景
Redis简介
更新问题
一:环境配置
1.1: 在pom.xml文件中添加依赖
1.2:配置SpringBoot核心配置文件application.properties
二:在Config文件夹中创建RedisConfig配置文件类
2.1:RedisTemplate中的几个角色:
2.2:为什么要自定义序列化:
2.2.1:Spring 中提供了以下几个序列化器:
四:封装Redis Utils工具包
4.1:RedisUtils.java
4.2:RedisKeys.java
4.3:UserRedis.java
五:流程实现
1.RedisTestController
2.RedisTestService
3.RedisTestServiceImpl
4.AttributeData
调用结果:
将一些经常展现和不会频繁变更的数据,存放在存取速率更快的地方。 缓存就是一个存储器,在技术选型中,常用 Redis 作为缓存数据库,可以帮我们分散掉数据库的压力,有了它能更好的支持并发性能,主要是在获取资源方便性能优化的关键方面。可以这样理解redis位于数据库和springboot框架之间,起到数据缓存的作用。
更新缓存模式 Cache aside
这是最常用最常用的pattern了。其具体逻辑如下:
Jedis
、Lettuce
。既然 Lettuce
和 Jedis
的都是连接 Redis Server 的客户端,那么它们有什么区别呢?这里说说为什么要添加 org.apache.commons 依赖,如果不加,它会报错:Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool2.impl.GenericObjectPoolConfig
org.springframework.boot
spring-boot-starter-data-redis
1.4.7.RELEASE
org.apache.commons
commons-pool2
2.8.0
spring:
redis:
open: true # 是否开启redis缓存 true开启 false关闭
database: 0
host: 127.0.0.1
port: 3304
password: 123456 # 密码(默认为空)
timeout: 6000ms # 连接超时时长(毫秒)
expire: 3600 #7天不过期
lettuce:
pool:
max-active: 100 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 20 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
#Redis
##Redis数据库索引
spring.redis.database=0
##Redis服务器地址
spring.redis.host=127.0.0.1
## Redis服务器连接端口
spring.redis.port=3304
## 连接超时时间(毫秒)
spring.redis.timeout=3
## Redis服务器连接密码(默认为空)
spring.redis.password=135246
## 连接池中的最大连接数 (使用复数则标识没有限制) 默认 8
spring.redis.pool.max.active=100
## 连接池最大阻塞等待时间(使用负值表示没有限制)默认 -1
spring.redis.pool.max.wait=-1
## 连接池中的最大空闲连接 默认 8
spring.redis.pool.max.idle=20
## 连接池中的最小空闲连接 默认 0
spring.redis.pool.max.idle=0
RedisTemplate 是 Spring 操作 Redis 的重点内容。 RedisTemplate是一个强大的类,首先它会自动从 RedisConnectionFactory 工厂中获取连接,然后执行对应的 Redis命令,提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现,在最后还会关闭 Redis 的连接。
RedisTemplate操作时,默认会采用jdkSerializable序列化机制,使得插入的值在redis客户端看来会有乱码 类似于: "\xac\ced\x00\x05t\x00\x03key" ,所以解决这个问题就需要修改默认的序列化规则。
本章使用的是StringRedisSerializer, String序列化方式。
RedisConfig 所在结构地址:
package com.lizexin.springbootdemo.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* 项目名称:springboot-demo
* 类名称:RedisConfig
* 类描述:Redis配置
* 创建时间:2023/08/04
* @author lzx
* @version v1.0
*/
@Configuration
public class RedisConfig {
@Autowired
private RedisConnectionFactory factory;
@Bean
public RedisTemplate redisTemplate() {
// 将template 泛型设置为
RedisTemplate redisTemplate = new RedisTemplate<>();
// 使用 String 序列化方式,序列化 KEY。
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 使用 String 序列化方式,序列化 VALUE。
redisTemplate.setValueSerializer(new StringRedisSerializer());
// 使用 String 序列化方式,序列化 HashKEY。
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
// 使用 String 序列化方式,序列化 ValueKEY。
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
// 配置连接工厂
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
/**
* HashOperations
* 操作 Hash 类型数据
**/
@Bean
public HashOperations hashOperations(RedisTemplate redisTemplate) {
return redisTemplate.opsForHash();
}
/**
* HashOperations
* 操作 String 类型数据
**/
@Bean
public ValueOperations valueOperations(RedisTemplate redisTemplate) {
return redisTemplate.opsForValue();
}
/**
* HashOperations
* 操作 List 类型数据
**/
@Bean
public ListOperations listOperations(RedisTemplate redisTemplate) {
return redisTemplate.opsForList();
}
/**
* HashOperations
* 操作 Set 类型数据
**/
@Bean
public SetOperations setOperations(RedisTemplate redisTemplate) {
return redisTemplate.opsForSet();
}
/**
* HashOperations
* 操作 SortedSet 类型数据
**/
@Bean
public ZSetOperations zSetOperations(RedisTemplate redisTemplate) {
return redisTemplate.opsForZSet();
}
}
Redis工具包分为三个类
1:RedisUtils.java Redis方法类主要记录对redis的一些操作,增删改查等。
2:RedisKeys.java Redis自定义Key类,自定义配置,对redis操作时好分辨哪个key的数据
3:UserRedis.java 封装类,将RedisUtils和RedisKey进行封装,用户直接操作此类
redis 工具包 所在结构地址:
package com.lizexin.springbootdemo.utils.redis;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* 项目名称:springboot-demo
* 类名称:RedisUtils
* 类描述:Redis工具类
* 创建时间:2023/08/04
* @author lzx
* @version v1.0
*/
@Component
public class RedisUtils {
/**日志*/
private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private ValueOperations valueOperations;
@Autowired
private HashOperations hashOperations;
@Autowired
private ListOperations listOperations;
@Autowired
private SetOperations setOperations;
@Autowired
private ZSetOperations zSetOperations;
/**默认过期时长,单位: 秒*/
public final static long DEFAULT_EXPIRE = 60 * 10;
/**从配置文件获取 默认过期时长*/
@Value("${spring.redis.expire}")
public long expire;
/**不设置过期时长 */
public final static long NOT_EXPIRE = -1;
/**它可以帮助我们快速的进行各个类型和Json类型的相互转换*/
private static final ObjectMapper MAPPER = new ObjectMapper();
/**给指定key设置固定时间的有效期*/
public void expireAt(String key,Date date){
redisTemplate.expireAt(key,date);
}
/**根据指定的key,获取过期时间*/
public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS);}
/**判断key是否存在*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存, @param key可以传一个值 或多个
* 该注解屏蔽某些编译时的警告信息
* */
@SuppressWarnings("unchecked")
public void delete(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection) CollectionUtils.arrayToList(key));
}
}
}
/**将Object值放如缓存并设置默认时间,调用下一个方法将值转为JSON字符串*/
public void set(String key, Object value){
set(key, value, DEFAULT_EXPIRE);
}
/**将Object值转为JSON字符串放入缓存,并设置过期时长*/
public void set(String key, Object value, long expire){
valueOperations.set(key, objectToJson(value));
if(expire != NOT_EXPIRE){
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
}
/**根据key和泛型获取值, 调用下一个get方法*/
public T get(String key, Class clazz) {
return get(key, clazz, NOT_EXPIRE);
}
/**根据key键获取值并转为对象 并重新设置过期时间*/
public T get(String key, Class clazz, long expire) {
String value = valueOperations.get(key);
if(expire != NOT_EXPIRE){
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
//将Json字符串转换为bean对象
return value == null ? null : fromJson(value, clazz);
}
/**根据key键获取值返回为String*/
public String get(String key, long expire) {
String value = valueOperations.get(key);
if(expire != NOT_EXPIRE){
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
}
return value;
}
/*根据key获取值*/
public String get(String key) {
return get(key, NOT_EXPIRE);
}
/**Object转换为JSON字符串,在存reid的时候调用此方法*/
private String objectToJson(Object object){
if(object instanceof Integer || object instanceof Long || object instanceof Float ||
object instanceof Double || object instanceof Boolean || object instanceof String){
return String.valueOf(object);
}
return JSONUtil.toJsonStr(object);
}
/**JSON字符串, 转成javaBean对象*/
private T fromJson(String json, Class clazz){
return JSONUtil.toBean(json, clazz);
//return JSON.parseObject(json,clazz);
}
/**将JsonObject转为实体类对象,转换异常将被抛出*/
public static T fromJsonToBean(JSONObject json, Class beanClass) {
return null == json ? null : json.toBean(beanClass);
}
/**将元素添加到指定set集合中*/
public void addToSet(String key,String member){
redisTemplate.opsForSet().add(key,member);
}
/**批量添加到指定set集合中*/
public void addBatchToSet(String key,List memberList) {
Object[] members = memberList.toArray();
redisTemplate.opsForSet().add(key,members);
}
/**统计指定set的长度,当指定key的set不存在时,返回null*/
public Long countForSet(String key){
return redisTemplate.opsForSet().size(key);
}
/**只有当key不存在时才设置key的值,并返回true;当key存在时不修改key的值,并返回false。*/
public Boolean isMember(String value){
return setOperations.isMember(RedisKeys.AutoKey,value);
}
/**向集合添加值并设置过期时间*/
public Long addSetDataExpire(String value,String name,long expire){
Long addSet = setOperations.add(name,value);
if(expire != NOT_EXPIRE){
redisTemplate.expire(name, expire, TimeUnit.SECONDS);
}
return addSet;
}
/**向右边批量添加元素*/
public boolean addrightPushAll(String key, List
package com.zhangtao.moguding.province.utils.redis;
/**
* 项目名称:user-center-service
* 类名称:RedisKeys
* 类描述:redis所有的key
* 创建时间:2023/7/27
*
* @author lzx
* @version v1.0
*/
public class RedisKeys {
//最大蘑菇号的key
public final static String MAX_MOGUNO_KEY = "moguding:user:max_mogu_no";
//短信验证码的key
public static String getSmsCodeKey(String key,Integer type){
return "moguding:user:smsCode:" + key+":"+type;
}
//权限列表
public final static String PERMISSIONS_USERAUTH_KEY = "moguding:permissions:permissions_userauth_list:";
//参数配置
public static String getUserConfigKey(String... key){
return "moguding:user:config:" + key;
}
//用户Token
public final static String AUTH_TOKEN_KEY = "moguding:user:authToken:";
//authtoken的key web端
public static String getAuthToken(String type,String userid){
if("web".equals(type)){
return AUTH_TOKEN_KEY+type+":" + userid;
}else {
return getAuthToken(userid);
}
}
//authtoken的key app端
public static String getAuthToken(String userid){
return AUTH_TOKEN_KEY+ userid;
}
//缓存活跃蘑菇号key的
public final static String ACTIVE_MOGU_NO= "moguding:user:active:";
//缓存今日学校签到排行榜
public final static String SCHOOL_SIGN_RANK= "moguding:school:sign:rank";
//学校统计(实时发送【考勤,上岗,周报】)
public final static String SCHOOL_COUNT= "moguding.school.count";
//自动报告key
public final static String AutoKey = "autoReport_set";
//30天最后一次考勤
public final static String LastSign = "moguding.last.sign";
//省平台基础数据
public static String getProvinceConfigKey(String key){
return "moguding:province:config:" + key;
}
}
package com.zhangtao.moguding.province.utils.redis;
import com.zhangtao.moguding.province.entity.UserConfigEntity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 项目名称:user-center-service
* 类名称:UserRedis
* 类描述:UserRedis
* 创建时间:2019/5/27
* @author lzx
* @version v1.0
*/
/*
*将RedisUtils的set方法和RedisKeys的key 封装到一起
* */
@Component
public class UserRedis {
@Autowired
private RedisUtils redisUtils;
public void set(String key,String value) {
if(key == null){
return ;
}
String redisKey = RedisKeys.getUserConfigKey(key);
redisUtils.set(redisKey, value,redisUtils.expire);
}
public void delete(String key) {
if(key == null){
return ;
}
String redisKey = RedisKeys.getUserConfigKey(key);
redisUtils.delete(redisKey);
}
public String get(String key){
if(key == null){
return null;
}
String redisKey = RedisKeys.getUserConfigKey(key);
return redisUtils.get(redisKey);
}
public UserConfigEntity getObject(String key){
if(key == null){
return null;
}
String redisKey = RedisKeys.getUserConfigKey(key);
return redisUtils.get(redisKey, UserConfigEntity.class);
}
//向Redis添加值,设置默认过期时长 7天, set方法将value进行序列化(转为JSON字符串)
/* public void set(String key,Object value) {
if(key == null){
return ;
}
String redisKey = RedisKeys.getRedisTestKey(key);
redisUtils.set(redisKey, value,redisUtils.expire);
}
public T get(String key, Class clazz){
if(key == null){
return null;
}
String redisKey = RedisKeys.getRedisTestKey(key);
return redisUtils.get(redisKey,clazz);
}
//判断Redis测试key是否存在
public boolean hasKey(String key){
if(key == null){
return false;
}
String redisKey = RedisKeys.getRedisTestKey(key);
return redisUtils.hasKey(redisKey);
};*/
//将今日活跃用户的蘑菇号批量存进redis指定Set集合中
public void addUserMoguNosToSet(List moguNos){
redisUtils.addBatchToSet(RedisKeys.ACTIVE_MOGU_NO,moguNos);
}
//将今日活跃用户的蘑菇号缓存Set集合清空
public void deleteForCacheUserMoguNo(){
redisUtils.delete(RedisKeys.ACTIVE_MOGU_NO);
}
//判断Redis测试key是否存在
public boolean hasKey(String key){
if(key == null){
return false;
}
String redisKey = RedisKeys.getProvinceConfigKey(key);
return redisUtils.hasKey(redisKey);
};
//从redis中统计活跃用户数量
public int countActiveUser(){
Long Lnum = redisUtils.countForSet(RedisKeys.ACTIVE_MOGU_NO);
if(Lnum==null){
return 0;
}else {
return Lnum.intValue();
}
}
//省级平台获取redisKey
public String getProvinceKey(String key){
if(key == null){
return null;
}
String redisKey = RedisKeys.getProvinceConfigKey(key);
return redisUtils.get(redisKey);
}
//省级平台向redis插入数据
public void setProvinceDataToRedis(String key,String list){
String keys = RedisKeys.getProvinceConfigKey(key);
//配置文件默认保留时长
redisUtils.setRedis(keys,list,redisUtils.expire);
}
}
模拟SpringBoot项目结构调用
package com.lizexin.springbootdemo.Controller;
import com.lizexin.springbootdemo.entity.Item;
import com.lizexin.springbootdemo.service.RedisTestService;
import com.zhangtao.common.dto.response.BaseResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @program: springboot-demo
* @author: lzx
* @Time: 2023/8/9 16:35
* @description: Redis测试接口
* @version: v1.0
*/
@RestController
@Api(tags = {"Redis测试接口"},produces = "RedisTest_controller")
@RequestMapping("/redis")
public class RedisTestController {
@Autowired
RedisTestService redisTestService;
private Logger logger = LoggerFactory.getLogger(RedisTestController.class);
@ApiOperation(value = "Redis测试,将对象插入缓存",notes = "")
@RequestMapping("/v1/test")
public BaseResponse redisInsertBeanController(@RequestBody Item item){
return redisTestService.redisInsertBeanService(item);
}
@ApiOperation(value = "Redis测试,将List插入缓存",notes = "")
@RequestMapping("/v2/test")
public BaseResponse redisInsertListController(@RequestBody Item item){
return redisTestService.redisInsertListService(item);
}
@ApiOperation(value = "Redis测试,将Map>插入缓存,取出来转Map",notes = "")
@RequestMapping("/v3/test")
public BaseResponse redisInsertMapController(@RequestBody Item item){
return redisTestService.redisInsertMapService(item);
}
}
package com.lizexin.springbootdemo.service;
import com.lizexin.springbootdemo.entity.Item;
import com.zhangtao.common.dto.response.BaseResponse;
/**
* @program: springboot-demo
* @author: lzx
* @Time: 2023/8/9 22:55
* @description: Redis测试接口Service
* @version: v1.0
*/
public interface RedisTestService {
BaseResponse redisInsertBeanService( Item item);
BaseResponse redisInsertListService( Item item);
BaseResponse redisInsertMapService( Item item);
}
package com.lizexin.springbootdemo.service.impl;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.lizexin.springbootdemo.entity.Item;
import com.lizexin.springbootdemo.service.RedisTestService;
import com.lizexin.springbootdemo.utils.*;
import com.lizexin.springbootdemo.utils.redis.UserRedis;
import com.zhangtao.common.dto.response.BaseResponse;
import com.zhangtao.common.dto.response.ObjectResponse;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* @program: springboot-demo
* @author: lzx
* @Time: 2023/08/9 22:58
* @description: test
* @version: v1.0
*/
@Service
public class RedisTestServiceImpl implements RedisTestService {
@Autowired
UserRedis userRedis;
private static final Logger logger = LoggerFactory.getLogger(RedisTestServiceImpl.class);
@Override
@ApiOperation(
"通过key得到值并重新设置过期时间,若值不存在则重新插入缓存。"+
"set方法封装了 JSONUtil.toJsonStr"+ "get带泛型的方法封装了JSONUtil.toBean "
)
public BaseResponse redisInsertBeanService(Item item) {
String redisKey= "redisInsertBeanService";
System.out.println(redisKey);
//判断key值是否存在,如果存在则优先取缓存
if (userRedis.hasKey(redisKey)){
Item jsonString= userRedis.get(redisKey,Item.class);
logger.info("存在值");
logger.info(jsonString.toString());
return ObjectResponse.resObj(jsonString);
}else{
//不存在则缓存
Item item1= AttributeData.list9();
logger.info("不存在值 插入");
userRedis.set(redisKey,item1);
return ObjectResponse.ok();
}
}
@Override
@ApiOperation("get方法不带泛型默认返回Json字符串,需要自行反序列化")
public BaseResponse redisInsertListService(Item item) {
//通过key得到值,
String redisKey = "redisInsertListService";
System.out.println(redisKey);
//判断key值是否存在,如果存在则优先取缓存
if (userRedis.hasKey(redisKey)){
List- list = JSONArray.parseArray(userRedis.get(redisKey),Item.class);
logger.info("存在值");
logger.info(list.toString());
return ObjectResponse.resObj(list);
}else{
//不存在则缓存
List
- list= AttributeData.list8();
logger.info("不存在值 插入");
userRedis.set(redisKey,list);
return ObjectResponse.ok();
}
}
@Override
@ApiOperation("")
public BaseResponse redisInsertMapService(Item item) {
//通过key得到值,
String redisKey= "redisInsertMapService";
System.out.println(redisKey);
//判断key值是否存在,如果存在则优先取缓存
if (userRedis.hasKey(redisKey)){
String jsonString= userRedis.get(redisKey);
//可以通过JSonString转对象方法把Vlue值从Set转为JsonArray
Map
arrayMap= JSONUtil.toBean(jsonString,Map.class);
logger.info("存在值");
logger.info(arrayMap.toString());
return ObjectResponse.resObj(arrayMap);
}else{
//不存在则缓存
List- list= AttributeData.list10();
//根据key转map,之后将Value换成set集合
//将集合添加至Map 指定参数作为key
Map
> map = new HashMap();
Map>setMap =new HashMap<>();
map = list.stream().collect(Collectors.groupingBy(Item::getName,Collectors.toList()));
for (Map.Entry> key:map.entrySet()){
Set set = new HashSet<>();
key.getValue().forEach(c->{
set.add(c.getValue());
});
setMap.put(key.getKey(), set);
}
logger.info("不存在值 插入");
userRedis.set(redisKey,setMap);
return ObjectResponse.ok();
}
}
}
package com.lizexin.springbootdemo.utils;
import com.lizexin.springbootdemo.dto.CommonInterfaceDto;
import com.lizexin.springbootdemo.dto.InformationDatasDto;
import com.lizexin.springbootdemo.dto.export.ExportGxySchoolFlowDto;
import com.lizexin.springbootdemo.entity.Item;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @program: springboot-demo
* @author: lzx
* @Time: 2023/8/9 22:55
* @description: 常用数据集
* @version: v1.0
*/
public class AttributeData {
public static List