1. AOP實作redis快取
1.1 業務需求
需要通過自定義注解的形式動態實作快取操作.通過注解獲取其中的key.超時時間.

1.2 自定義注解的用法

1.3 編輯CacheAOP
package com.jt.aop;
import com.jt.annotation.CacheFind;
import com.jt.pojo.ItemDesc;
import com.jt.util.ObjectMapperUtil;
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.boot.autoconfigure.cache.CacheProperties;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import sun.misc.Cache;
import java.util.Arrays;
@Aspect //標識我是一個切面
@Component //將物件交給spring容器管理 cacheAOP
public class CacheAOP {
@Autowired
private Jedis jedis;
/**
* 實作思路:
* 1.動態獲取key 用戶自定義的前綴+用戶的引數[0,xx]
* 2.判斷key是否存在
* 存在: 直接從redis中獲取資料 JSON, 需要將json轉化為具體物件 按斬訓傳值型別進行轉化
* 不存在: 執行目標方法.獲得回傳值資料. 將回傳值結果轉化為JSON格式. 之后保存到快取中.
* @param joinPoint
* @return
*/
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws NoSuchMethodException {
Object result = null;
//1.獲取key的前綴
String key = cacheFind.key();
//2.獲取方法引數
String argString = Arrays.toString(joinPoint.getArgs());
key = key + "::" + argString;
try {
//3.判斷快取中是否有資料
if(jedis.exists(key)){
String json = jedis.get(key);
//5.獲取回傳值型別
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
result = ObjectMapperUtil.toObject(json,methodSignature.getReturnType());
System.out.println("AOP查詢redis快取");
}else{
//表示快取中沒有資料,執行目標方法
result = joinPoint.proceed();
String json = ObjectMapperUtil.toJSON(result);
//4.判斷資料中是否有超時時間
if(cacheFind.seconds()>0){
jedis.setex(key,cacheFind.seconds(),json);
}else{
jedis.set(key,json);
}
System.out.println("AOP執行資料庫呼叫!!!!!");
}
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new RuntimeException(throwable);
}
return result;
}
//切面 = 切入點運算式 + 通知方法
//運算式1: bean(itemCatServiceImpl) ItemCatServiceImpl類
//@Pointcut("bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service.*)")
// .* 一級包下的類 ..* 所有子孫后代的包和類
//回傳值型別任意, com.jt.service包下的所有類的add方法引數型別任意型別
//寫引數型別時注意型別的大小寫
/* @Pointcut("execution(* com.jt.service..*.*(..))")
public void pointcut(){
}*/
/**
*
* joinPoint代表連接點物件,一般適用于前四大通知型別(除around之外的)
* 客人 路人
*/
/*@Before("pointcut()")
public void before(JoinPoint joinPoint){
//1.獲取目標物件
Object target = joinPoint.getTarget();
System.out.println(target);
//2.獲取目標物件的路徑 包名.類名.方法名
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
System.out.println("目標方法的路徑:"+(className+"."+methodName));
//3.獲取引數型別
System.out.println(Arrays.toString(joinPoint.getArgs()));
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint joinPoint){
System.out.println("環繞通知執行");
Object data = null;
try {
data = joinPoint.proceed(); //執行目標方法
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return data;
}*/
}
1.4 商品分類名稱優化
優化商品分類名稱的名稱. 在業務層添加快取注解.

2 Redis屬性說明
2.1 Redis持久化策略
2.1.1 為什么要持久化
Redis中的記錄都保存在記憶體中,如果記憶體斷電或者服務器宕機,則記憶體資料直接丟失.業務中不允許發生. 所以需要將資料定期進行維護.
2.1.2 RDB模式
說明: RDB模式是Redis的默認的持久化策略.無需手動的開啟.
特點:
1.Redis會定期的執行RDB持久化操作. 缺點:可能導致記憶體資料丟失.
2.RDB記錄的是記憶體資料的快照,并且后續的快斬訓覆寫之前的快照.每次只保留最新資料.效率更高.
命令:
1).save 命令 要求立即執行持久化操作 save會造成執行緒的阻塞.
2).bgsave 命令 后臺執行持久化操作 后臺運行不會造成阻塞. 異步操作, 不能保證立即執行
2.1.3 AOF模式
說明: AOF模式默認條件下是關閉的,需要手動的開啟,如果開啟了AOF模式則RDB模式將失效.但是如果手動執行save命令,則也會生成RDB檔案.
1).開啟AOF模式

特點:
1.AOF模式記錄程式的執行的程序.所以可以保證資料不丟失.
2.由于AOF記錄程式運行的程序,所以整個持久化檔案相對大,所以需要定期維護. 效率低
2.1.4 RDB與AOF模式持久化對比
1).RDB模式
save 900 1 如果在900秒內,執行了一次更新操作則持久化一次
save 300 10
save 60 10000 操作越快 ,持久化的周期越短.
2).AOF模式
appendfsync always 用戶執行一次更新操作,則持久化一次 異步操作
appendfsync everysec 每秒操作一次
appendfsync no 不主動操作 一般不用.
2.1.5 關于RDB與AOF總結
策略: 如果資料允許少量丟失,首選RDB模式,
如果資料不允許丟失則首選AOF模式.
企業策略: 又要滿足效率,同時滿足資料不丟失.
主機: 采用RDB模式
從機: 采用AOF模式
2.1.6面試題
題目: 小麗是公司特別漂亮的妹子,誤操作將redis服務器執行了flushAll命令,問你作為專案經理如何處理??
A. 訓斥一頓,之后HR開除.
B. 秀一下自己的技術,讓小麗崇拜 一起過上了幸福的生活
解決方案: 需要將從庫中的AOF檔案 進行編輯,洗掉多余的flushAll命令,之后重啟redis即可.
問題2: 小麗在執行完上述操作之后,由于好奇 誤將aof檔案一并洗掉,問如何處理???
答: 殺人祭天!!!
2.2 Redis記憶體策略
2.2.1 LRU演算法
LRU是Least Recently Used的縮寫,即最近最少使用,是一種常用的頁面置換演算法,選擇最近最久未使用的頁面予以淘汰,該演算法賦予每個頁面一個訪問欄位,用來記錄一個頁面自上次被訪問以來所經歷的時間 t,當須淘汰一個頁面時,選擇現有頁面中其 t 值最大的,即最近最少使用的頁面予以淘汰,
判斷維度: 時間T
2.2.2 LFU演算法
LFU(least frequently used (LFU) page-replacement algorithm),即最不經常使用頁置換演算法,要求在頁置換時置換參考計數最小的頁,因為經常使用的頁應該有一個較大的參考次數,但是有些頁在開始時使用次數很多,但以后就不再使用,這類頁將會長時間留在記憶體中,因此可以將參考計數暫存器定時右移一位,形成指數衰減的平均使用次數,
判斷維度: 使用次數
2.2.3 隨機演算法
隨機演算法
2.2.4 TTL演算法
將剩余時間短的資料,提前洗掉.
2.2.5 Redis的記憶體優化策略
- volatile-lru 在設定超時時間的資料中采用LRU演算法
- allkeys-lru 所有的資料采用LRU演算法洗掉
- volatile-lfu 設定了超時時間的資料采用LFU演算法洗掉
- allkeys-lfu 所有資料采用LFU演算法洗掉
- volatile-random 設定了超時時間的資料采用隨機演算法
- allkeys-random 所有資料的隨機演算法
- volatile-ttl 設定了超時時間之后采用TTL演算法
- noeviction 不做任何操作,只是回傳報錯資訊.

2.3 關于Redis常見面試題
業務場景: 高并發環境下.用戶長時間對服務器進行操作,可能產生如下的問題.
2.3.1 什么是快取穿透
說明: 用戶高并發環境下訪問資料庫和快取中都不存在的資料稱之為快取穿透現象.

解決方案:
1). 禁用IP 限制IP訪問.
2). 限流 每秒最多訪問3次
3). 布隆過濾器
布隆過濾器
布隆過濾器(Bloom Filter)是1970年由布隆提出的,它實際上是一個很長的二進制向量和一系列隨機映射函式,布隆過濾器可以用于檢索一個元素是否在一個集合中,它的優點是空間效率和查詢時間都比一般的演算法要好的多,缺點是有一定的誤識別率和洗掉困難,
原理:

布隆過濾器優化:
問題:如何解決hash碰撞問題
知識點: 由于hash碰撞問題,可能由多個key有相同的位置,所以得出結論,布隆過濾器認為資料存在,那么資料可能存在.如果布隆過濾器認為資料不存在,則資料一定不在.
如何降低hash碰撞的幾率:
答:
1.擴容二進制向量位數.
2.增加hash函式的個數
當位數增加/函式適當增加,則可以有效的降低hash碰撞的幾率. 默認值 0.03

2.3.2 什么是快取擊穿
說明: **某個(一個)熱點資料在快取中突然失效.**導致大量的用戶直接訪問資料庫.導致并發壓力過高造成例外.

解決方案:
1.盡可能將熱點資料的超時時間 設定的長一點
2.設定多級快取 超時時間采用隨機演算法.

2.3.3 什么是快取雪崩
說明: 在快取服務器中,由于大量的快取資料失效,導致用戶訪問的命中率過低.導致直接訪問資料庫.
問題分析:
1. fluashAll命令可能導致快取雪崩.
2. 設定超時時間時,應該采用隨機演算法
3. 采用多級快取可以有效防止.

3 redis分片機制
3.1 redis性能優化
說明: 單臺redis記憶體容量是有限的.但是如果有海量的資料要求實作快取存盤,則應該使用多個Redis節點.

3.2 Redis分片機制定義

3.3 Redis分片機制配置
3.3.1 配置規劃
說明: 準備3臺redis服務器 分別為 6379/6380/6381
3.3.2 準備3個組態檔

修改各自埠號 改為6380 6381

3.3.3 啟動redis服務器

3.3.4 檢查redis啟動狀態

3.3.5 redis分片入門案例

3.3 Redis分片機制配置
說明:根據redis節點個數.拼接字串
#配置單臺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
3.4 編輯RedisConfig配置類
@Configuration //標識我是一個配置類 一般與@Bean注解聯用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
@Value("${redis.nodes}")
private String nodes; //node,node,node
@Bean
public ShardedJedis shardedJedis(){
List<JedisShardInfo> shards = new ArrayList<>();
String[] nodeArray = nodes.split(",");
for( String node :nodeArray){ //node=host: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);
}
}
3.5 修改CacheAOP中的注入

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/232503.html
標籤:其他
