1 Redis的分片技術
1.1 分片介紹
1.1.1 傳統方式的問題
說明:如果采用單台redis時,如果redis出現宕機現象.那么會直接影響我們的整個的服務.
1.1.2 采用分片模式
說明:由一台redis擴展到多台redis.由多台redis共同為用戶提供服務.並且每台redis中保存1/N的數據.
好處:如果一台redis出現了問題.不會影響整個redis的服務.
1.1.3 配置多台redis
說明:將redis.conf文件拷貝3份到shard文件夾下.分別形成6379/6380/6381的文件
cp redis.conf shard/redis-6379.conf
1.1.4 修改端口
1.將端口改為6380
2.將端口號為6381
1.1.5 啟動多台redis
redis-server redis-6379.conf
redis-server redis-6380.conf
redis-server redis-6381.conf
- 進入客戶端
redis-cli -p 6380
- 關閉客戶端
redis-cli -p 6380 shutdown
1.1.6 分片測試
@Test public void test02(){ /** * 定義分片的連接池 * 參數介紹 * 1.poolConfig 定義鏈接池的大小 * 2.shards 表示List<Shardinfo> 表示redis的信息的集合 * * 補充知識: * Jedis 引入會有線程安全性問題.所以通過線程池的方式動態獲取 * jedis鏈接. */ //定義鏈接池大小 JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(1000); poolConfig.setTestOnBorrow(true);//測試鏈接是否正常,如果不正常會重新獲取 poolConfig.setMaxIdle(30); //定義分片的list集合 List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>(); shards.add(new JedisShardInfo("192.168.126.142",6379)); shards.add(new JedisShardInfo("192.168.126.142",6380)); shards.add(new JedisShardInfo("192.168.126.142",6381)); ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, shards); //獲取redis的鏈接 ShardedJedis jedis = jedisPool.getResource(); for(int i=1;i<=20;i++){ jedis.set("KEY"+i, ""+i); } System.out.println("redis插入操作成功!!!"); }
1.2 Redis分片的算法介紹
1.2.1 哈希一致性算法
說明:根據節點的信息進行哈希計算計算的結果在0-2^32-1 的范圍內.計算完成之后能夠進行動態的節點的掛載
特點:
- 均衡性
a) 盡可能的讓數據均衡的落入緩存區
- 單調性
a) 如果數據已經進行了動態的分配.如果有新節點引入能夠進行動態的分配.
- 分散性
a) 由於分布式開發,用戶不能看到緩存的全部.那么數據計算的結果能位置不固定.好的哈希一致性算法應該盡可能的降低分散性
- 負載
a) 從另一個角度研究分散性.由於用戶不能看到緩存區的全部,經過計算后,同一個緩存區可以會有多個數據.這樣的方式不是特別的友好,所以高級的哈希算法應該盡可能降低負載.
1.2.2 圖示
說明:
- 通過節點的ip+編號通過hash運算,算出具體的位置
- 如果需要進行key的存儲.首先根據key值進行計算,找到位置,之后進行數據的存儲
- 按照順時針的方向,數據找最近的節點進行掛載.
- 取數據時通過node信息.獲取數據.
- 以上的節點(node)信息都是動態的
1.2.3 均衡性
說明:由於哈希計算可能會出現節點位置較近的現象.通過均衡性中虛擬節點.盡可能的讓數據達到平均
1.2.4 單調性
說明:由於node節點信息可能會出現變化,數據可以動態的掛載到新的節點中.
如果節點需要刪除.那么該節點中的數據會動態的掛載到新的節點中,保證數據是有效的
1.2.5 分散性
說明:由於采用了分布式的部署,某些用戶不能夠看到全部的緩存區(node)節點信息.這時在進行數據操作時,可會出現同一key位於不同的位置.
1.2.6 負載
說明:從另外的一個角度考慮分散性.同一個位置.可以存放不同的key
要求:好的hash算法應該盡可能的降低分散性和負載.
1.3 Spring的方式整合分片
1.3.1 編輯配置文件
說明:將原有redis的配置文件改名,為了不讓Spring容器掃描解析出錯.
圖上的配置文件保留原有的配置前邊多加字母A.可以保證spring容器不會加載該配置
<!--通過線程池的方式整合單台redis --> <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!--定義連接的總數 --> <property name="maxTotal" value="${redis.maxTotal}"/> <!--定義最大的空閑數量 --> <property name="maxIdle" value="${redis.maxIdle}"/> <!--定義最小空閑數量 --> <property name="minIdle" value="${redis.minIdle}"></property> </bean> <!-- List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>(); shards.add(new JedisShardInfo("192.168.126.142",6379)); shards.add(new JedisShardInfo("192.168.126.142",6380)); shards.add(new JedisShardInfo("192.168.126.142",6381)); ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, shards); --> <!--定義jediShardinfo對象 --> <bean id="host1" class="redis.clients.jedis.JedisShardInfo"> <constructor-arg index="0" value="${redis.host1}" type="java.lang.String"/> <constructor-arg index="1" value="${redis.port1}" type="int"/> </bean> <bean id="host2" class="redis.clients.jedis.JedisShardInfo"> <constructor-arg index="0" value="${redis.host2}" type="java.lang.String"/> <constructor-arg index="1" value="${redis.port2}" type="int"/> </bean> <bean id="host3" class="redis.clients.jedis.JedisShardInfo"> <constructor-arg index="0" value="${redis.host3}" type="java.lang.String"/> <constructor-arg index="1" value="${redis.port3}" type="int"/> </bean> <bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool"> <constructor-arg index="0" ref="poolConfig" /> <constructor-arg index="1"> <list> <ref bean="host1"/> <ref bean="host2"/> <ref bean="host3"/> </list> </constructor-arg> </bean>
1.3.2 編輯redis.properties
1.3.3 編輯RedisService
1.3.4 編輯業務層Service
說明:ItemCatService中通過Spring依賴注入的形式進行賦值.所以不需要修改.
思想(面向接口編程的好處)
2 Redis哨兵
2.1 搭建主從結構
2.1.1 配置主從
說明:配置主從時需要啟動主從的redis節點信息.之后進行主從掛載.
2.1.2 主從掛載
- 復制新的配置文件,后重新啟動新的redis節點信息
- 查詢節點的狀態
127.0.0.1:6380> info replication
實現掛載
127.0.0.1:6380> SLAVEOF 192.168.126.142 6379
通過該命令實現掛載
- 將6381掛載到6379中
SLAVEOF 192.168.126.142 6379
2.1.3 主從測試
說明:通過6379查詢info信息.並且通過set命令為6379賦值后檢測從機中是否含有數據.
2.1.4 哨兵的原理
工作原理:
1.用戶鏈接時先通過哨兵獲取主機Master的信息
2.獲取Master的鏈接后實現redis的操作(set/get)
3.當master出現宕機時,哨兵的心跳檢測發現主機長時間沒有響應.這時哨兵會進行推選.推選出新的主機完成任務.
4.當新的主機出現時,其余的全部機器都充當該主機的從機
2.1.5 哨兵的配置
1.將保護模式關閉
2.配置哨兵的監控
sentinel monitor mymaster 192.168.126.142 6379 1
mymaster:主機的名稱
192.168.126.142:主機的IP
6379 :表示主機的端口號
1:表示有幾個哨兵選舉后生效
3修改時間
說明:當10秒后主機沒有響應則選舉新的主機
4.修改推選時間
2.1.6 啟動哨兵
說明:當啟動哨兵后,由於沒有后台啟動所以需要重新開啟連接進行測試
redis-sentinel sentinel-6379.conf
將redis主機宕機后檢測哨兵是否工作
測試結果.當redis主機宕機后,哨兵自動選舉出新的主機
檢測6380是否為主機 info replication
2.1.7 多台哨兵測試
1.哨兵編譯后的文件
cp sentinel-6379.conf sentinel-6380.conf
- 修改端口
- 修改序列號
說明:由於哨兵啟動時會自動的生成序列號.序列號相同時哨兵不能相互通訊.所以修改為不同的
- 修改哨兵的個數
2.1.8 多台哨兵測試
說明:關閉redis主機后,檢測哨兵是否能夠生效,選舉新的主機.如果能夠選舉新的主機表示多台哨兵搭建完成.
2.1.9 測試報錯
說明:由於配置了哨兵,redis中配置了主從結構.所以從機不能進行寫庫操作,.如需測試分片需要關閉redis重新開啟分片的redis
2.2 Spring整合哨兵
2.2.1 測試用例
//哨兵的測試 @Test public void test03(){ //創建哨兵的連接池 //String類型表示的是哨兵的IP:端口號 Set<String> sentinels = new HashSet<String>(); String msg = new HostAndPort("192.168.126.142",26379).toString(); System.out.println("通過對象輸出哨兵的信息格式:"+msg); //為set集合賦值 保存全部的哨兵信息 sentinels.add(new HostAndPort("192.168.126.142",26379).toString()); sentinels.add(new HostAndPort("192.168.126.142",26380).toString()); sentinels.add(new HostAndPort("192.168.126.142",26381).toString()); /** * 參數介紹 * 1.masterName 表示鏈接哨兵的主機的名稱一般是字符串mymaster * 2.sentinels 哨兵的集合Set<> */ JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", sentinels); Jedis jedis = sentinelPool.getResource(); jedis.set("name", "tom"); System.out.println("獲取數據:"+jedis.get("name")); }
2.3 Spring整合哨兵
2.3.1 編輯配置文件
<bean id="jedisSentinelPool" class="redis.clients.jedis.JedisSentinelPool"> <constructor-arg index="0" value="${redis.masterName}"/> <constructor-arg index="1"> <set> <value>${redis.sentinel.host1}</value> <value>${redis.sentinel.host2}</value> <value>${redis.sentinel.host3}</value> </set> </constructor-arg> </bean>
2.3.2 配置哨兵的配置文件
2.3.3 編輯工具類代碼
說明:在工具類中定義方法.之后測試查看效果