此工程爲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的路由
把做分片處理的邏輯抽取出來,運行一個獨立的代理服務,客戶端連接到這個代理服務,代理服務做請求的轉發
基於服務端實現