1. Redis分片機制
1.1 爲何須要分片機制
若是須要存儲海量的內存數據,若是隻使用一臺redis,沒法保證redis工做的效率. 大量的時間都浪費到了尋址當中.因此須要一種機制可以知足該要求.
採用分片機制實現:
java
1.2 Redis分片搭建
1.2.1 搭建注意事項
Redis服務的啓動須要依賴於redis.conf的配置文件. 若是須要準備3臺redis.則須要準備3個redis.conf的配置.node
準備端口號:
1.6379
2.6380
3.6381
redis
1.2.2 分片實現
修改端口號: 將各自的端口號進行修改.
算法
啓動3臺redis服務器
校驗服務器是否正常運行
spring
1.2.3 關於分片的注意事項
1.問題描述:
當啓動多臺redis服務器以後,多臺redis暫時沒有必然的聯繫,各自都是獨立的實體.能夠數據數據的存儲.如圖所示.
2.若是將分片經過程序的方式進行操做,要把3臺redis當作一個總體,因此與上述的操做徹底不一樣.不會出現一個key同時保存到多個redis的現象.
數據庫
1.3 分片入門案例
/** * 測試Redis分片機制 * 思考: shards 如何肯定應該存儲到哪臺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")); }
1.4 一致性hash算法
1.4.0 常識說明
常識1: 通常的hash是8位16進制數. 0-9 A-F (24)8 = 2^32
常識2: 若是對相同的數據進行hash運算 結果必然相同的.
常識3: 一個數據1M 與數據1G的hash運算的速度一致.
json
1.4.1 一致性hash算法介紹
一致性哈希算法在1997年由麻省理工學院提出,是一種特殊的哈希算法,目的是解決分佈式緩存的問題。 [1] 在移除或者添加一個服務器時,可以儘量小地改變已存在的服務請求與處理請求服務器之間的映射關係。一致性哈希解決了簡單哈希算法在分佈式哈希表( Distributed Hash Table,DHT) 中存在的動態伸縮等問題 [2] 。
數組
1.4.2 特性1-平衡性
概念:平衡性是指hash的結果應該平均分配到各個節點,這樣從算法上解決了負載均衡問題 [4] 。(大體平均)
問題描述: 因爲節點都是經過hash方式進行算計.因此可能出現如圖中的現象.,致使負載嚴重不平衡
解決方法: 引入虛擬節點
緩存
1.4.3 特性2-單調性
特色: 單調性是指在新增或者刪減節點時,不影響系統正常運行 [4] 。
服務器
1.4.4 特性3-分散性
諺語: 雞蛋不要放到一個籃子裏.
③分散性是指數據應該分散地存放在分佈式集羣中的各個節點(節點本身能夠有備份),沒必要每一個節點都存儲全部的數據 [4]
1.5 SpringBoot整合Redis分片
1.5.1 編輯配置文件
# 配置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
1.5.2 編輯配置類
@Configuration @PropertySource("classpath:/properties/redis.properties") public class JedisConfig { @Value("${redis.nodes}") private String nodes; //node,node,node..... //配置redis分片機制 @Bean public ShardedJedis shardedJedis(){ nodes = nodes.trim(); //去除兩邊多餘的空格 List<JedisShardInfo> shards = new ArrayList<>(); String[] nodeArray = nodes.split(","); for (String strNode : nodeArray){ //strNode = host:port String host = strNode.split(":")[0]; int port = Integer.parseInt(strNode.split(":")[1]); JedisShardInfo info = new JedisShardInfo(host, port); shards.add(info); } return new ShardedJedis(shards); } }
1.5.3 修改AOP注入項
2 Redis哨兵機制
2.1 關於Redis分片說明
優勢: 實現內存數據的擴容.
缺點: 若是redis分片中有一個節點出現了問題.,則整個redis分片機制用戶訪問必然有問題 直接影響用戶的使用.
解決方案: 實現redis高可用.
2.2 配置redis主從的結構
策略劃分: 1主2從 6379主 6380/6381從
1.將分片的目錄複製 更名位sentinel
- 重啓三臺redis服務器
3.檢查redis節點的主從的狀態
4.實現主從掛載
5.檢查主機的狀態
2.3 哨兵的工做原理
原理說明:
1.配置redis主從的結構.
2.哨兵服務啓動時,會監控當前的主機. 同時獲取主機的詳情信息(主從的結構)
3.當哨兵利用心跳檢測機制(PING-PONG) 連續3次都沒有收到主機的反饋信息則判定主機宕機.
4.當哨兵發現主機宕機以後,則開啓選舉機制,在當前的從機中挑選一臺Redis當作主機.
5.將其餘的redis節點設置爲新主機的從.
2.4 編輯哨兵配置文件
1).複製配置文件
cp sentinel.conf sentinel/
2).修改保護模式
3).開啓後臺運行
4).設定哨兵的監控
其中的1 表示投票生效的票數 當前只有一個哨兵因此寫1
5).修改宕機的時間
6).選舉失敗的時間
說明:若是選舉超過指定的時間沒有結束,則從新選舉.
7).啓動哨兵服務
2.5 Redis哨兵高可用實現
測試步驟:
1.檢查主機的狀態
2.將redis主服務器宕機 等待10秒 以後檢查從機是否當選新的主機
3.重啓6379服務器., 檢查是否成爲了新主機的從.
2.6 哨兵入門案例
/** * 測試Redis哨兵 */ @Test public void testSentinel(){ Set<String> set = new HashSet<>(); //1.傳遞哨兵的配置信息 set.add("192.168.126.129:26379"); JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster",set); Jedis jedis = sentinelPool.getResource(); jedis.set("aa","哨兵測試"); System.out.println(jedis.get("aa")); }
2.7 SpringBoot整合Redis哨兵 (10分鐘)
2.7.1 編輯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.sentinel=192.168.126.129:26379
2.7.2 編輯redis配置類
@Configuration @PropertySource("classpath:/properties/redis.properties") public class JedisConfig { @Value("${redis.sentinel}") private String sentinel; //暫時只有單臺 @Bean public JedisSentinelPool jedisSentinelPool(){ Set<String> sentinels = new HashSet<>(); sentinels.add(sentinel); return new JedisSentinelPool("mymaster",sentinels); } }
2.7.3 修改CacheAOP中的注入項
package com.jt.aop; import com.jt.anno.CacheFind; import com.jt.config.JedisConfig; import com.jt.util.ObjectMapperUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisSentinelPool; import redis.clients.jedis.ShardedJedis; import java.lang.reflect.Method; import java.util.Arrays; @Aspect //我是一個AOP切面類 @Component //將類交給spring容器管理 public class CacheAOP { @Autowired //private Jedis jedis; //單臺redis //private ShardedJedis jedis; //分片機制 private JedisSentinelPool jedisSentinelPool; /** * 切面 = 切入點 + 通知方法 * 註解相關 + 環繞通知 控制目標方法是否執行 * * 難點: * 1.如何獲取註解對象 * 2.動態生成key prekey + 用戶參數數組 * 3.如何獲取方法的返回值類型 */ @Around("@annotation(cacheFind)") //參數傳遞變量的傳遞 //@Around("@annotation(com.jt.anno.CacheFind)") public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){ //從池中獲取jedis對象 Jedis jedis = jedisSentinelPool.getResource(); Object result = null; try { //1.拼接redis存儲數據的key Object[] args = joinPoint.getArgs(); String key = cacheFind.preKey() +"::" + Arrays.toString(args); //2. 查詢redis 以後判斷是否有數據 if(jedis.exists(key)){ //redis中有記錄,無需執行目標方法 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{ //表示數據不存在,須要查詢數據庫 result = joinPoint.proceed(); //執行目標方法及通知 //將查詢的結果保存到redis中去 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(); } jedis.close(); //將使用完成的連接記得關閉. return result; } }
做業
1.預習 Redis集羣搭建步驟 2.瞭解redis集羣工做原理