=============java
1). 自定義註解 @CacheFind(key=「xxx」,second=-1)
2). 使用自定義註解 標識業務方法 將方法的返回值保存到緩存中.
3). 利用AOP 攔截註解 利用環繞通知方法實現業務面試
package com.jt.aop; import com.jt.anno.CacheFind; 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.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import java.lang.reflect.Method; import java.util.Arrays; /*@Service @Controller @Repository*/ @Component //組件 將類交給spring容器管理 @Aspect //表示我是一個切面 public class RedisAOP { @Autowired private Jedis jedis; /* * 實現AOP業務調用 * 1.攔截指定的註解 * 2.利用環繞通知實現 * 實現步驟: * 1.獲取KEY 必須先獲取註解 從註解中獲取key? * 2.校驗redis中是否有值 * * * 3.知識點補充: * 指定參數名稱進行傳值,運行期綁定參數類型完成註解的攔截 * joinPoint必須位於參數的第一位. */ @Around("@annotation(cacheFind)") public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){ Object result = null; //key=業務名稱::參數 String key = cacheFind.key(); String args = Arrays.toString(joinPoint.getArgs()); key = key + "::" + args; //2.校驗是否有值 if(jedis.exists(key)){ String json = jedis.get(key); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Class returnType = methodSignature.getReturnType(); result = ObjectMapperUtil.toObj(json,returnType); System.out.println("AOP查詢redis緩存"); }else{ //redis中沒有數據,因此須要查詢數據庫,將數據保存到緩存中 try { result = joinPoint.proceed(); String json = ObjectMapperUtil.toJSON(result); //是否設定超時時間 if(cacheFind.seconds()>0){ jedis.setex(key, cacheFind.seconds(), json); }else{ jedis.set(key,json); } System.out.println("AOP查詢數據庫"); } catch (Throwable throwable) { throwable.printStackTrace(); } } return result; } /** * //1.獲取key 註解 方法對象 類 方法名稱 參數 * Class targetClass = joinPoint.getTarget().getClass(); * //2.獲取方法對象 * String methodName = joinPoint.getSignature().getName(); * Object[] args = joinPoint.getArgs(); * Class[] classArgs = new Class[args.length]; * for(int i=0;i<args.length;i++){ * classArgs[i] = args[i].getClass(); * } * try { * //反射實例化對象 * Method method = targetClass.getMethod(methodName,classArgs); * CacheFind cacheFind = method.getAnnotation(CacheFind.class); * String key = cacheFind.key(); * System.out.println(key); * } catch (NoSuchMethodException e) { * e.printStackTrace(); * } */ //公式 aop = 切入點表達式 + 通知方法 //@Pointcut("bean(itemCatServiceImpl)") //@Pointcut("within(com.jt.service.*)") //@Pointcut("execution(* com.jt.service.*.*(..))") //.* 當前包的一級子目錄 /* @Pointcut("execution(* com.jt.service..*.*(..))") //..* 當前包的全部的子目錄 public void pointCut(){ }*/ //如何獲取目標對象的相關參數? //ProceedingJoinPoint is only supported for around advice /* @Before("pointCut()") public void before(JoinPoint joinPoint){ //鏈接點 Object target = joinPoint.getTarget(); Object[] args = joinPoint.getArgs(); String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); System.out.println("目標對象:"+target); System.out.println("方法參數:"+Arrays.toString(args)); System.out.println("類名稱:"+className); System.out.println("方法名稱:"+methodName); }*/ }`
===============redis
說明: Redis中將數據都保存到了內存中,可是內存的特色斷電及擦除. 爲了保證redis中的緩存數據不丟失,則須要將內存數據按期進行持久化操做.
持久化: 將內存數據,寫到磁盤中.算法
特色:
1.RDB模式是Redis默認的持久化規則.
2.RDB模式記錄的是Redis內存數據快照(只保留最新數據)
3.RDB模式按期持久化(時間可調) 可能會致使數據丟失.
4.RDB模式備份效率是最高的.
5.RDB模式備份阻塞式的 在備份時不容許其餘用戶操做. 保證數據安全性. save
命令:
1.主動備份 save 會阻塞用戶操做
2.後臺備份 bgsave 異步的方式進行持久化操做 不會阻塞.spring
1.save 900 1 900秒內,用戶執行了一次更新操做時,那麼就持久化一次
2.save 300 10 300秒內,用戶執行了10次更新操做. 那麼就持久化一次
3.save 60 10000 60秒內,用戶執行了10000次的更新操做,則持久化一次.
4.save 1 1 1秒內 1次更新 持久化一次!! 性能特別低.
數據庫
默認的條件下,持久化文件名稱 dump.rdb
json
./ 表明當前文件目錄. 意義使用絕對路徑的寫法.
後端
1).AOF模式默認的條件下是關閉狀態.須要手動開啓.
2).AOF模式記錄的是用戶的操做過程. 能夠實現實時持久化.保證數據不丟失.
3).AOF模式維護的持久化文件佔用的空間較大.因此持久化效率不高. 而且須要按期的維護持久化文件.
4).AOF模式一旦開啓,則redis以AOF模式爲主 讀取的是AOF文件.api
1).開啓AOF模式
2).持久化策略
always: 用戶更新一次,則持久化一次.
everysec: 每秒持久化一次 效率更高
no: 不主動持久化. 操做系統有關. 幾乎不用.
數組
業務場景:
小麗是一個特別漂亮的實習生.你是他的項目主管. 因爲小麗業務不熟,在生產環境中無心執行了flushAll操做. 問如何補救??
場景1: redis中的服務只開啓了默認的持久策略 RDB模式.
解決方案:
1.關閉現有的redis服務器.
2.檢查RDB文件是否被覆蓋. 若是文件沒有覆蓋.則重啓redis便可.(但願渺茫)
3.若是flushAll命令,同時執行了save操做,則RDB模式無效.
`場景2: redis中的服務開啓了AOF模式. 解決方案: 1.關閉redis服務器. 2.編輯redis 持久化文件 將flushAll命令刪除. 3.重啓redis服務器 通常條件下: RDB模式和AOF模式都會開啓. 經過save命令執行rdb持久化方式.
1).redis運行環境在內存中,純內存操做.
2).單線程操做 避免頻繁的上下文切換. 避免了開關連接的開銷.
3).採用了非阻塞I/O(BIO|NIO) 多路複用的機制(動態感知).
4). Redis最新版本 6.0版本 6.0之前的版本都是單線程操做方式. 6.0之後支持多線程操做方式. (執行時依舊是單線程操做).
若是頻繁使用redis,不停的向其中保存數據,而且不作刪除操做,則內存必然溢出. 可否優化內存策略.
可否自動的刪除不用的數據,讓redis中保留熱點數據!!!.
LRU是Least Recently Used的縮寫,即最近最少使用,是一種經常使用的頁面置換算法,選擇最近最久未使用的頁面(數據)予以淘汰。該算法賦予每一個頁面一個訪問字段,用來記錄一個頁面自上次被訪問以來所經歷的時間 t,當須淘汰一個頁面時,選擇現有頁面中其 t 值最大的,即最近最少使用的頁面予以淘汰。
計算維度: 自上一次以來所經歷的時間T.
說明:LRU算法是內存優化中最好用的算法.
LFU(least frequently used (LFU) page-replacement algorithm)。即最不常用頁置換算法,要求在頁置換時置換引用計數最小的頁,由於常用的頁應該有一個較大的引用次數。可是有些頁在開始時使用次數不少,但之後就再也不使用,這類頁將會長時間留在內存中,所以能夠將引用計數寄存器定時右移一位,造成指數衰減的平均使用次數。
維度: 引用次數
常識: 計算機左移 擴大倍數
計算機右移 縮小倍數
隨機刪除數據.
說明:將剩餘存活時間排序,將立刻要被刪除的數據,提早刪除.
說明1: Redis中採用的策略按期刪除+惰性刪除策略
說明2:
1.按期刪除: redis默認每隔100ms 檢查是否有過時的key, 檢查時隨機的方式進行檢查.(不是檢查全部的數據,由於效率過低.)
問題: 因爲數據衆多,可能抽取時沒有被選中.可能出現 該數據已經到了超時時間,可是redis並無立刻刪除數據.
問題: 因爲數據衆多, 用戶不可能將全部的內存數據都get一遍.必然會出現 須要刪除的數據一直保留在內存中的現象.佔用內存資源.
3.能夠採用上述的內存優化手段,主動的刪除.
內存優化算法說明:
1.volatile-lru 在設定超時時間的數據 採用LRU算法進行優化
2.allkeys-lru 在全部的數據採用LRU算法進行優化
3.volatile-lfu 在設定了超時時間的數據中採用LFU算法優化
4.allkeys-lfu 在全部的數據中採用LFU算法進行優化
5.volatile-random 在設定了超時時間的數據 採用隨機算法
6.allkeys-random 全部數據採用 隨機算法
7.volatile-ttl 設定超時時間的TTl算法
8.noeviction 不主動刪除數據,若是內存溢出則報錯返回.
=============
說明: 單臺redis存儲的數據容量有限的. 若是須要存儲海量的緩存數據,則使用單臺redis確定不能知足要求.爲了知足數據擴容的需求.則能夠採用分片的機制實現.
分別準備3臺redis 6379/6380/6381
說明: 將redis的配置文件放到shards目錄中.
修改配置文件端口號 依次修改6380/6381
啓動3臺redis:
redis-server 6379.conf
redis-server 6380.conf
redis-server 6381.conf
校驗服務器:
`package com.jt.test; import org.junit.jupiter.api.Test; import redis.clients.jedis.JedisShardInfo; import redis.clients.jedis.ShardedJedis; import java.util.ArrayList; import java.util.List; public class TestRedisShards { @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); //3臺redis當作1臺使用 內存容量擴大3倍. 79/80/81??? shardedJedis.set("shards", "redis分片測試"); System.out.println(shardedJedis.get("shards")); } }
一致性哈希算法在1997年由麻省理工學院提出,是一種特殊的哈希算法,目的是解決分佈式緩存的問題。 [1] 在移除或者添加一個服務器時,可以儘量小地改變已存在的服務請求與處理請求服務器之間的映射關係。一致性哈希解決了簡單哈希算法在分佈式哈希表( Distributed Hash Table,DHT) 中存在的動態伸縮等問題 [2] 。
做用: 解決緩存數據,在哪存儲的問題…
常識:
①平衡性是指hash的結果應該平均分配到各個節點,這樣從算法上解決了負載均衡問題 [4] 。
說明:經過虛擬節點實現數據的平衡
②單調性是指在新增或者刪減節點時,不影響系統正常運行 [4] 。
原則: 若是節點新增/減小 應該儘量保證原始數據儘量不變.
③分散性是指數據應該分散地存放在分佈式集羣中的各個節點(節點本身能夠有備份),沒必要每一個節點都存儲全部的數據 [4]
將數據分散存儲,即便未來服務器宕機,則影響只是一部分,.而不是所有.
諺語: 雞蛋不要放到一個籃子裏.
說明:Redis分片機制,雖然能夠實現Redis Redis內存擴容,可是redis 節點並沒有實現高可用.若是節點宕機,則整合redis分片將不可以使用.
規定: 6379主機 /6380/6381 從機
1.redis-server 6379.conf
2.redis-server 6380.conf
3.redis-server 6381.conf
命令1.: slaveof host port
命令說明: 在從機中執行上述命令 掛載的是主機的地址.
命令2: info replication
主從結構關係:
原理說明:
1.哨兵監控主機的運行的狀態. 經過心跳檢測機制(PING-PONG)若是連續3次節點沒有響應,則判定主機宕機,哨兵開始進行選舉.
2.哨兵經過連接主機,獲取主機的相關配置信息(包含主從結構),挑選連接當前主機的從機.根據隨機算法挑選出新的主機. 而且將其餘的節點設置爲新主機的從.
1).複製哨兵的配置文件
2).關閉保護模式
3).開啓後端運行
4).設定哨兵的投票數
5).修改選舉的超時時間
6).修改哨兵的狀態
哨兵命令: redis-sentinel sentinel.conf
檢查redis服務:
redis高可用測試:
1.關閉redis主機6379
2.等待10秒 檢查6380/6381到底誰是主機.
3.重啓6379服務器,檢查是否充當了新主機的從
`@Test public void test01(){ //定義哨兵的集合信息 Set<String> sentinels = new HashSet<>(); sentinels.add("192.168.126.129:26379"); //定義連接池信息 JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(200); //連接池 最多200個連接 poolConfig.setMaxIdle(20); //最大空閒連接數20 poolConfig.setMinIdle(10); //最小空閒連接數10 JedisSentinelPool pool = new JedisSentinelPool("mymaster",sentinels,poolConfig); //動態獲取jedis連接 Jedis jedis = pool.getResource(); jedis.set("abc", "redis賦值操做"); System.out.println(jedis.get("abc")); jedis.close(); }
將哨兵的機制交給SpringBoot管理.
1.分片機制: 能夠實現內存數據的擴容. 可是自己沒有實現高可用的效果.
2.哨兵機制: 哨兵能夠實現redis節點的高可用.可是哨兵自己沒有實現高可用的效果.
需求: 1.不依賴第三方實現高可用
2.實現內存數據的擴容
3.各個節點能夠高可用.
注意事項:
1.啓動6個redis節點
2.保證redis節點中的數據都是null的
3.根據報錯提示 排查問題. 檢查IP地址 檢查防火牆…
1). 關閉全部的Redis服務器
sh stop.sh
2).檢查redis配置文件
3).刪除多餘文件
1).關閉7000節點
redis-cli -p 7000 shutdown
2).檢查主從狀態
3).重啓7000 檢查狀態