Redis
Redis介绍
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
nginx: 3-5万/秒
redis: 读: 11.2万/秒 写: 8.6万/秒 平均10万/秒
吞吐量: 50万/秒
redis的作用
优化现有架构
说明:通过缓存服务器可以有效的提升用户的访问的效率.
注意事项:
1.缓存的数据结构 应该选用 K-V结构 只要key唯一 那么结果必然相同…
2.缓存中的数据不可能一直存储,需要定期将内存数据进行优化 LRU算法…
3.缓存要求运行速度很快, C语言实现… 运行在内存中.
4.如果缓存运行的数据在内存中,如果断电/宕机,则内存数据直接丢失. 实现内存数据的持久化操作(磁盘).
redis官网
http://www.redis.cn/
Redis安装(Linux)
1).上传redis.tar.gz文件至Linux系统的工作目录
cd /usr/local/src
2).解压redis文件
命令:tar -xvf 文件名
3).移动文件/修改文件至指定文件夹方便管理
移动命令:mv 文件名 目录名/
改名命令:mv 源文件名 新文件名
3).安装Redis
命令1: make
命令2: make install
修改redis.conf配置文件
1.修改IP绑定
2.关闭保护模式
3.开启后台启动
Redis基础命令
1.启动redis redis-server redis.conf
2.检查redis服务项
3. 进入redis客户端
redis-cli -p 6379
4.关闭redis
1).命令 redis-cli -p 6379 shutdown
2).kill命令 kill -9 pid号
SpringBoot整合Redis
导入jar包
<!--spring整合redis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
入门测试
package com.jt;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
public class TestRedis {
/**
* 1.实现redis测试
* 报错检查:
* 1.检查redis.conf配置文件 1.ip绑定问题 2.保护模式问题 3.后台启动问题
* 2.检查redis启动方式 redis-server redis.conf
* 3.检查防火墙
* */
@Test
public void test01(){
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.set("2007", "redis入门案例");
System.out.println(jedis.get("2007"));
}
/**
* 我想判断是否有key数据,如果没有则新增数据,如果有则放弃新增 */
@Test
public void test02(){
Jedis jedis = new Jedis("192.168.126.129",6379);
// if(!jedis.exists("2007")){ //判断数据是否存在.
// jedis.set("2007", "测试案例2222");
// }
//setnx作用: 如果有数据,则不做处理.
jedis.setnx("2007", "测试高级用法");
System.out.println(jedis.get("2007"));
}
/**
* 需求:
* 向redis中添加一个数据.set-key-value,要求添加超时时间 100秒.
* 隐藏bug: 代码执行过程中,如果报错,则可能删除失败.
* 原子性: 要么同时成功,要不同时失败.
* 解决方法: 将入库操作/超时时间一齐设定. setex
*/
@Test
public void test03() throws InterruptedException {
Jedis jedis = new Jedis("192.168.126.129",6379);
//jedis.set("2007", "测试时间");
//隐藏含义: 业务需要 到期删除数据
//jedis.expire("2007", 100);
jedis.setex("2007", 100, "测试时间");
System.out.println(jedis.ttl("2007")+"秒");
}
/**
* 1.如果数据存在,则不操作数据 setnx
* 2.同时设定超时时间,注意原子性 setex
* 参数说明:
* 1. XX = "xx"; 只有key存在,则进行操作
* 2. NX = "nx"; 没有key,进行写操作
* 3. PX = "px"; 毫秒
* 4. EX = "ex"; 秒
*/
@Test
public void test04() throws InterruptedException {
Jedis jedis = new Jedis("192.168.126.129",6379);
SetParams setParams = new SetParams();
setParams.xx().ex(100);
jedis.set("2007", "bbbbb",setParams);
System.out.println(jedis.get("2007"));
}
}
@Test
public void testHash() throws InterruptedException {
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.hset("person", "id", "18");
jedis.hset("person", "name", "hash测试");
jedis.hset("person", "age", "2");
Map<String,String> map = jedis.hgetAll("person");
Set<String> set = jedis.hkeys("person"); //获取所有的key
List<String> list = jedis.hvals("person");
}
@Test
public void testList() throws InterruptedException {
Jedis jedis = new Jedis("192.168.126.129",6379);
jedis.lpush("list", "1","2","3","4");
System.out.println(jedis.rpop("list"));
}
@Test
public void testTx(){
Jedis jedis = new Jedis("192.168.126.129",6379);
//1.开启事务
Transaction transaction = jedis.multi();
try {
transaction.set("a", "a");
transaction.set("b", "b");
transaction.set("c", "c");
transaction.exec(); //提交事务
}catch (Exception e){
transaction.discard();
}
}
编辑pro配置文件
由于redis的IP地址和端口都是动态变化的,所以通过配置文件标识数据. 由于redis是公共部分,所以一般写到common项目中.
编辑配置类
package com.jt.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.Jedis;
@Configuration //标识我是配置类
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
@Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Bean
public Jedis jedis(){
return new Jedis(host, port);
}
}
对象与JSON转化
package com.jt;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jt.pojo.ItemDesc;
import org.junit.jupiter.api.Test;
import java.util.Date;
public class TestObjectMapper {
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 1.对象如何转化为JSON串的???
* 步骤:
* 1.获取对象的所有的getXXXX()方法.
* 2.将获取的getXXX方法的前缀get去掉 形成了json的key=xxx
* 3.通过getXXX方法的调用获取属性的值,形成了json的value的值.
* 4.将获取到的数据 利用json格式进行拼接 {key : value,key2:value2....}
* 2.JSON如何转化为对象???
* {lyj:xxx}
* 步骤:
* 1. 根据class参数类型,利用java的反射机制,实例化对象.
* 2. 解析json格式,区分 key:value
* 3. 进行方法的拼接 setLyj()名称.
* 4.调用对象的setXXX(value) 将数据进行传递,
* 5.最终将所有的json串中的key转化为对象的属性.
*
*
* @throws JsonProcessingException
*/
@Test
public void testTOJSON() throws JsonProcessingException {
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(100L).setItemDesc("测试数据转化")
.setCreated(new Date()).setUpdated(new Date());
//1.将对象转化为JSON
String json = MAPPER.writeValueAsString(itemDesc);
System.out.println(json);
//2.将json转化为对象 src:需要转化的JSON串, valueType:需要转化为什么对象
ItemDesc itemDesc2 = MAPPER.readValue(json, ItemDesc.class);
/**
* 字符串转化对象的原理:
*/
System.out.println(itemDesc2.toString());
}
}
转化工具API
package com.jt.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ObjectMapperUtil {
private static final ObjectMapper MAPPER = new ObjectMapper();
//将对象转化为JSON
public static String toJSON(Object target){
try {
return MAPPER.writeValueAsString(target);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
//将JSON转化为对象
//需求: 如果用户传递什么类型,则返回什么对象
public static <T> T toObject(String json,Class<T> targetClass){
try {
return MAPPER.readValue(json, targetClass);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
AOP实现Redis缓存
问题分析
1.如果直接将缓存业务,写到业务层中,如果将来的缓存代码发生变化,则代码耦合高,必然重写编辑代码.
2.如果其他的业务也需要缓存,则代码的重复率高,开发效率低.
解决方案: 采用AOP方式实现缓存.
AOP
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP实现步骤
公式: AOP(切面) = 通知方法(5种) + 切入点表达式(4种)
通知
1.before通知 在执行目标方法之前执行
2.afterReturning通知 在目标方法执行之后执行
3.afterThrowing通知 在目标方法执行之后报错时执行
4.after通知 无论什么时候程序执行完成都要执行的通知
上述的4大通知类型,不能控制目标方法是否执行.一般用来记录程序的执行的状态.
一般应用与监控的操作.
5.around通知(功能最为强大的) 在目标方法执行前后执行.
因为环绕通知可以控制目标方法是否执行.控制程序的执行的轨迹.
切入点表达式
1.bean(“bean的ID”) 粒度: 粗粒度 按bean匹配 当前bean中的方法都会执行通知.
2.within(“包名.类名”) 粒度: 粗粒度 可以匹配多个类
3.execution(“返回值类型 包名.类名.方法名(参数列表)”) 粒度: 细粒度 方法参数级别
4.@annotation(“包名.类名”) 粒度:细粒度 按照注解匹配
业务实现策略
1).需要自定义注解CacheFind
2).设定注解的参数 key的前缀,数据的超时时间.
3).在方法中标识注解.
4).利用AOP 拦截指定的注解.
5).应该使用Around通知实现缓存业务.
编辑自定义注解
@Target(ElementType.METHOD) //注解对方法有效
@Retention(RetentionPolicy.RUNTIME) //运行期有效
public @interface CacheFind {
public String preKey(); //定义key的前缀
public int seconds() default 0; //定义数据的超时时间.
}
方法中标识注解
编辑CacheAOP
package com.jt.aop;
import com.jt.anno.CacheFind;
import com.jt.util.ObjectMapperUtil;
import lombok.extern.apachecommons.CommonsLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import java.lang.reflect.Method;
import java.util.Arrays;
@Aspect //标识我是一个切面
@Component //交给Spring容器管理
public class CacheAOP {
@Autowired
private Jedis jedis;
/**
* 注意事项: 当有多个参数时,joinPoint必须位于第一位.
* 需求:
* 1.准备key= 注解的前缀 + 用户的参数
* 2.从redis中获取数据
* 有: 从缓存中获取数据之后,直接返回值
* 没有: 查询数据库之后再次保存到缓存中即可.
*
* 方法:
* 动态获取注解的类型,看上去是注解的名称,但是实质是注解的类型. 只要切入点表达式满足条件
* 则会传递注解对象类型.
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws Throwable {
Object result = null; //定义返回值对象
String preKey = cacheFind.preKey();
String key = preKey + "::" + Arrays.toString(joinPoint.getArgs());
//1.校验redis中是否有数据
if(jedis.exists(key)){
//如果数据存在,需要从redis中获取json数据,之后直接返回
String json = jedis.get(key);
//1.获取方法对象, 2.获取方法的返回值类型
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
//2.获取返回值类型
Class returnType = methodSignature.getReturnType();
result = ObjectMapperUtil.toObject(json,returnType);
System.out.println("AOP查询redis缓存!!!");
}else{
//代表没有数据,需要查询数据库
result = joinPoint.proceed();
//将数据转化为JSON
String json = ObjectMapperUtil.toJSON(result);
if(cacheFind.seconds() > 0){
jedis.setex(key, cacheFind.seconds(), json);
}else{
jedis.set(key,json);
}
System.out.println("AOP查询数据库!!!");
}
return result;
}
/* @Around("@annotation(com.jt.anno.CacheFind)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//1.获取目标对象的Class类型
Class targetClass = joinPoint.getTarget().getClass();
//2.获取目标方法名称
String methodName = joinPoint.getSignature().getName();
//3.获取参数类型
Object[] argsObj = joinPoint.getArgs();
Class[] argsClass = null;
//4.对象转化为class类型
if(argsObj.length>0){
argsClass = new Class[argsObj.length];
for(int i=0;i<argsObj.length;i++){
argsClass[i] = argsObj[i].getClass();
}
}
//3.获取方法对象
Method targetMethod = targetClass.getMethod(methodName,argsClass);
//4.获取方法上的注解
if(targetMethod.isAnnotationPresent(CacheFind.class)){
CacheFind cacheFind = targetMethod.getAnnotation(CacheFind.class);
String key = cacheFind.preKey() + "::"
+Arrays.toString(joinPoint.getArgs());
System.out.println(key);
}
Object object = joinPoint.proceed();
System.out.println("环绕开始后");
return object;
}
*/
/* //切面 = 切入点表达式 + 通知方法
//@Pointcut("bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service.ItemCatServiceImpl)")
//@Pointcut("within(com.jt.service.*)") // .* 一级包路径 ..* 所有子孙后代包
//@Pointcut("execution(返回值类型 包名.类名.方法名(参数列表))")
@Pointcut("execution(* com.jt.service..*.*(..))")
//注释: 返回值类型任意类型 在com.jt.service下的所有子孙类 以add开头的方法,任意参数类型
public void pointCut(){
}*/
/**
* 需求:
* 1.获取目标方法的路径
* 2.获取目标方法的参数.
* 3.获取目标方法的名称
*/
/* @Before("pointCut()")
public void before(JoinPoint joinPoint){
String classNamePath = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
System.out.println("方法路径:"+classNamePath);
System.out.println("方法参数:"+ Arrays.toString(args));
System.out.println("方法名称:"+methodName);
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint){
try {
System.out.println("环绕通知开始");
Object obj = joinPoint.proceed();
//如果有下一个通知,就执行下一个通知,如果没有就执行目标方法(业务方法)
System.out.println("环绕通知结束");
return null;
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new RuntimeException(throwable);
}
}*/
}
Redis持久化
说明:Redis运行环境在内存中,如果redis服务器关闭,则内存数据将会丢失.
需求: 如何保存内存数据呢?
解决方案: 可以定期将内存数据持久化到磁盘中.
持久化策略规则:
当redis正常运行时,定期的将数据保存到磁盘中,当redis服务器重启时,则根据配置文件中指定的持久化的方式,实现数据的恢复.(读取数据,之后恢复数据.)
RDB模式
1.2.1RDB模式特点说明
1).RDB模式是Redis默认的策略.
2).RDB模式能够定期(时间间隔)持久化. 弊端:可能导致数据的丢失.
3).RDB模式记录的是内存数据的快照.持久化效率较高. 快照只保留最新的记录.
RDB模式命令
1.save命令: 将内存数据持久化到磁盘中 主动的操作 会造成线程阻塞
2.bgsave命令: 将内存数据采用后台运行的方式,持久化到文件中. 不会造成阻塞.
3.默认的持久化的机制
save 900 1 如果在900秒内,执行了1次更新操作,则持久化一次
save 300 10 如果在300秒内,执行了10次更新操作,则持久化一次
save 60 10000 如果在60秒内,执行了10000次更新操作,则持久化一次
1.2.3 持久化文件配置
1).指定持久化文件
2).指定持久化文件目录
AOF模式
AOF模式特点
1).AOF模式默认条件下是关闭的.需要手动开启
2).AOF模式记录的是用户的操作过程,所以可以实现实时持久化操作.
3).AOF模式由于记录的是实时的操作过程,所以持久化文件较大.需要定期维护.
启动AOF模式
说明:如果一旦开启AOF模式,则以AOF模式为准.
持久化操作总结
1.当内存数据允许少量丢失时,采用RDB模式 (快)
2.当内存数据不允许丢失时,采用AOF模式(定期维护持久化文件)
3.一般在工作中采用 RDB+AOF模式共同作用,保证数据的有效性.
面试题
问题: 如果小李(漂亮妹子)在公司服务器中执行了flushAll命令,问怎么办?
答: 需要找到aof文件之后,删除flushAll命令 之后重启redis,执行save命令即可.
Redis分片机制
Redis分片说明
如果需要Redis存储海量的内存数据,使用单台redis不能满足用户的需求,所以可以采用Redis分片机制实现数据存储.
注意事项:
如果有多台redis,则其中的数据都是不一样的…
Redis部署
搭建端口:6379/6380/6381
1.准备配置文件:
6379.conf 6380.conf 6381.conf
2.修改端口号
分别修改80/81的端口即可
Redis分片测试
@Test
public void testShards(){
List<JedisShardInfo> shards = new ArrayList<>();
shards.add(new JedisShardInfo("192.168.126.129", 6379));
shards.add(new JedisShardInfo("192.168.126.129", 6380));
shards.add(new JedisShardInfo("192.168.126.129", 6381));
ShardedJedis shardedJedis = new ShardedJedis(shards);
shardedJedis.set("shards", "redis分片操作!!!");
System.out.println(shardedJedis.get("shards"));
}
Redis哨兵机制
问题说明: redis分片主要的作用是实现内存数据的扩容.但是如果redis分片中有一个节点宕机,则直接影响所有节点的运行. 能否优化?
实现策略: 采用Redis哨兵机制实现Redis节点高可用.
Redis节点主从配置
准备主从节点
1).关闭redis分片的节点,之后复制配置文件准备哨兵的配置.
2).复制分片的目录 改名为sentinel
3).删除多余的持久化文件,保存redis配置文件
4).启动3台Redis服务器
实现redis主从
命令: info replication 检查redis节点的状态信息
节点划分: 6379 当主机 6380/81 当从机
命令2: 实现主从挂载 slaveof host port
3).检查6379主机的状态
结论: redis所有节点都可以相同通信.并且路径当前的主从的状态.
数据主从同步的状态
哨兵机制工作原理
1).当哨兵启动时,会链接redis主节点,同时获取所有节点的状态信息
2).当哨兵连续3次通过心跳检测机制(PING-PONG),如果发现主机宕机,则开始选举.
3).哨兵内部通过随机算法筛选一台从机当选新的主机.其他的节点应该当新主机的从.
哨兵机制配置
关闭保护模式
开启后台运行
配置投票机制
修改投票时间
主机宕机3秒之后开始选举
哨兵高可用测试
1.启动哨兵 redis-sentinel sentinel.conf
2).检查哨兵配置
3)将主机宕机,之后检查从机是否当选主机
4).启动之前的主机,检查是否当选新主机的从
5).如果搭建异常 则删除重做
关闭所有redis服务器.
Redis 集群部署
问题分析:
分片/哨兵有哪些缺点
1.分片缺点: 分片的主要的功能是实现内存的扩容的. 但是没有高可用的效果.
2.哨兵缺点: 数据没有扩容,哨兵本身没有高可用机制
需求: 既可以实现内存数据的扩容,同时实现高可用机制(不用第三方).
Redis集群搭建问题说明
1.首先关闭所有的Redis服务器
2.检查配置文件编辑是否正确.
3.删除多余的配置文件
4.重启redis服务器
5.搭建redis集群
redis-cli --cluster create --cluster-replicas 1 192.168.126.129:7000 192.168.126.129:7001 192.168.126.129:7002 192.168.126.129:7003 192.168.126.129:7004 192.168.126.129:7005
Redis集群入门
@Test
public void testCluster(){
Set<HostAndPort> sets = new HashSet<>();
sets.add(new HostAndPort("192.168.126.129", 7000));
sets.add(new HostAndPort("192.168.126.129", 7001));
sets.add(new HostAndPort("192.168.126.129", 7002));
sets.add(new HostAndPort("192.168.126.129", 7003));
sets.add(new HostAndPort("192.168.126.129", 7004));
sets.add(new HostAndPort("192.168.126.129", 7005));
JedisCluster jedisCluster = new JedisCluster(sets);
jedisCluster.set("jedis", "集群赋值");
System.out.println(jedisCluster.get("jedis"));
}
面试题
1.redis集群中一共可以存储16384个KEY? 不对的
答: 16384只是槽位的数量 只负责规划这个数据归谁管理的问题.至于数据如何存储,是由redis内存决定的.
hash(key1) = 3000,
hash(key2) = 3000;
2.Redis集群中最多可以有多少台主机? 16384台主机.
3.Redis中如果遇到多线程操作,是否有线程安全性问题 ? 没有
因为:redis服务器是单进程单线程操作. 每次操作都是由一个线程执行,所以不会有线程安全性问题.
4.Redis如何实现内存数据的优化? LRU/LFU/随机算法/TTL
SpringBoot整合Redis集群
编辑properties文件
说明:将redis集群的节点写入pro配置文件中
# 配置单台redis服务器
#redis.host=192.168.126.129
#redis.port=6379
##配置redis分片
#redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381
# redis集群配置
redis.nodes=192.168.126.129:7000,192.168.126.129:7001,192.168.126.129:7002,192.168.126.129:7003,192.168.126.129:7004,192.168.126.129:7005
编辑配置类
package com.jt.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Configuration //标识我是配置类
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
//实现redis集群操作
@Value("${redis.nodes}")
private String nodes; //node,node,node
@Bean
public JedisCluster jedisCluster(){
Set<HostAndPort> nodeSet = new HashSet<>();
String[] nodeArray = nodes.split(",");
for (String node : nodeArray){ //host:port
String host = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
nodeSet.add(new HostAndPort(host,port));
}
return new JedisCluster(nodeSet);
}
/*@Bean
public ShardedJedis shardedJedis(){
List<JedisShardInfo> shards = new ArrayList<>();
String[] nodeArray = nodes.split(",");
for (String node : nodeArray){ //node=ip:port
String host = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
//准备分片节点信息
JedisShardInfo info = new JedisShardInfo(host, port);
shards.add(info);
}
return new ShardedJedis(shards);
}*/
/* @Value("${redis.host}")
private String host;
@Value("${redis.port}")
private Integer port;
@Bean
public Jedis jedis(){
return new Jedis(host, port);
}*/
}
编辑AOP配置
在AOP中注入Redis缓存对象