此工程为Spring boot项目
Redis切成编程
目标:在方法上添加一个注解,实现查询的时候缓存
方法:自定义注解+AOP切面编程
步骤
话不多说,直接上代码演示
第一步
定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface RedisCache {
//存储前缀
String name() default "Spl---";
//要存储的key,默认是查询条件的第一个参数
String key() default "";
//默认5分钟
int expireTime() default 5;
//默认值是以分钟为单位
TimeUnit unit() default TimeUnit.MINUTES;
}
第二步
注解使用的地方
第三步
AOP切面思想
@Component
@Aspect
@Slf4j
/**
* AOP切面编程
*/
public class RedisCacheAop {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 切入点
*/
@Pointcut("@annotation(com.trc.study.annotation.RedisCache)")
public void pointCup(){}
/**
* 增强方法-环绕
* @param joinPoint
* @return
* @throws Throwable
*/
@Around(value = "pointCup()")
public Object cache(ProceedingJoinPoint joinPoint) throws Throwable{
//从切入点,获取方法签名
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
//从切点,获取目标对象的字节码,的方法。参数:(方法名,方法的所有参数)
RedisCache redisCache = method.getAnnotation(RedisCache.class);
String key = redisCache.key();
Object[] args = joinPoint.getArgs();
String prefix = redisCache.name();
String methodName = method.getName();
//获取key值
String cacheKey = getCacheKey(key, args, prefix, methodName);
Object proceed = null;
//根据key从缓存获取值
String value = stringRedisTemplate.opsForValue().get(cacheKey);
//判断value是否为空
if (StringUtils.isNotBlank(value)){
log.info("从redis里面获取数据!");
return getValueActualTypeData(method,value);
}else {
log.info("从mysql里面获取数据!");
//查询数据库,执行原来的方法
proceed = joinPoint.proceed();
//封装结果
String results = JSONObject.toJSONString(proceed);
int expireTime = redisCache.expireTime();
TimeUnit timeUnit = redisCache.unit();
//存入缓存
stringRedisTemplate.opsForValue().set(cacheKey,results,expireTime,timeUnit);
return proceed;
}
}
/**
* 获取key值
* @param key
* @param args
* @param prefix
* @param methodName
* @return
*/
private String getCacheKey(String key,Object[] args,String prefix,String methodName){
String cacheKey = "";
if (StringUtils.isNotBlank(prefix)) {
cacheKey = "/" + prefix + "/";
}
if (StringUtils.isNotBlank(key)) {
return cacheKey += key;
}
if (null != args && 0 < args.length) {
return cacheKey += args[0];
}
return methodName;
}
/**
* 获取数据
* @param method
* @param value
* @return
* @throws ClassNotFoundException
*/
private Object getValueActualTypeData(Method method, String value) throws ClassNotFoundException {
Class returnActualType = getReturnActualType(method);
if (null != returnActualType) {
return JSONObject.parseArray(value, returnActualType);
}
return null;
}
/**
* 获取返回值类型
* @param method
* @return
* @throws ClassNotFoundException
*/
private Class getReturnActualType(Method method) throws ClassNotFoundException {
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType) {
Type[] actualTypes = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualType : actualTypes) {
return Class.forName(actualType.getTypeName());
}
}
return null;
}
}
其他:工程结构
mvc模式
配置文件
server.port=9009
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root
mybatis.type-aliases-package=com.trc.study.common
mapper.mappers=com.trc.study.dao.RedisCacheMapper
logging.level.info
#redis配置
#Redis服务器地址
spring.redis.host=127.0.0.1
#Redis服务器连接端口
spring.redis.port=6379
#Redis数据库索引(默认为0)
spring.redis.database=0
#连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=50
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=3000
#连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=20
#连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=2
#连接超时时间(毫秒)
spring.redis.timeout=5000
redis基本数据类型
数据类型
String: 字符串
可以用来存储字符串、整数、浮点数
缓存
数据共享分布式
分布式锁
全局ID
计数器
限流
Hash: 散列
包含键值对的无序散列表。value只能是字符串,不能嵌套其他类型
存储对象类型的数据
List: 列表
存储有序的字符串(从左到右),元素可以重复。可以充当队列和栈的角色
用户消息时间线timeline
Set: 集合
String类型的无序集合,最大存储数量2^32-1(40亿左右)
随机获取元素
ZSet: 有序集合
sorted set,有序的set,每个元素有个score。
score相同时,按照key的ASCII码排序。
BitMaps
是在字符串类型上面定义的位操作。一个字节由8个二进制位组成。
Hyperloglogs
提供了一种不太准确的基数统计方法
Streams
5.0推出的数据类型。支持多播的可持久化的消息队列,用于实现发布订阅功能
Redis 为什么这么快
1)纯内存结构
KV结构的内存数据库,时间复杂度O(1)
2)单线程
1、没有创建线程、销毁线程带来的消耗
2、避免了上线文切换导致的CPU消耗
3、避免了线程之间带来的竞争问题,例如加锁释放锁死锁等等
3)多路复用
“多路”指的是多个网络连接,“复用”指的是复用同一个线程
利用select、poll、epoll可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll是只轮询那些真正发出了事件的流),依次顺序的处理就绪的流,这种做法就避免了大量的无用操作
主从、哨兵
集群
可用性、数据安全、性能
主从
从节点不能写入数据(只读),只能从 master 节点同步数据
主从复制原理
连接阶段
数据同步阶段
命令传播阶段
不足
RDB文件过大的情况下,同步非常耗时
如果主服务器挂了,对外提供的服务就不可用
哨兵
通过运行监控服务器来保证服务的可用性
会对Sentinel做集群的部署。Sentinel既监控所有的Redis服务,Sentinel之间也相互监控
通信
SDOWN与ODOWN转换过程
Sentinel与slaves"自动发现"机制
Leader选举
分布式方案
Redis数据的分片
在客户端实现相关的逻辑,例如用取模或者一致性哈希对key进行分片,查询和修改都先判断key的路由
把做分片处理的逻辑抽取出来,运行一个独立的代理服务,客户端连接到这个代理服务,代理服务做请求的转发
基于服务端实现