1 Redis持久化策略
1.1 策略說明
說明:
redis作爲內存數據庫,需要保持大量用戶的數據。但是redis中的數據保存在內存中,一旦遇上斷電/宕機,肯定會導致內存數據丟失。
所以爲了防止數據丟失,redis採用持久化的方式,實現內存數據的備份。
策略說明:
當redis內存中有數據時,自身支持兩種策略模式:
- RDB模式
- AOF模式
redis根據相應模式的設置進行數據持久化,且RDB模式和AOF模式都有各自的持久化文件。當redis重啓時,redis會根據配置文件中指定的持久化文件名稱,加載持久化文件,爲內存恢復數據。
1.2 RDB模式
1.2.1 模式說明
RDB模式定期將內存中的數據持久化。
如果用戶允許丟失少量的數據則首選RDB模式,因爲RDB模式定期爲內存做快照,該方式的備份速度很快。
1.2.2 備份命令
**說明:**執行redis備份命令需要在redis客戶端執行。
命令:
手動保存
save
後臺備份
bgsave
區別:
save 會造成線程阻塞,只有當備份完成時,才允許執行redis更新操作。
bgsave 表示後臺運行,不會造成線程阻塞,挑當前不忙的時間做數據備份。
1.2.3 備份方式
save 900 1
在900秒內,redis做一次更新操作,則備份一次
save 300 10
在300秒內,redis做10次更新操作,則備份一次
save 60 10000
在60秒內,如果redis做10000次操作則備份一次
1.2.4 配置文件說明
1.持久化文件名稱
2.持久化文件保存路徑
dir ./
表示當前文件夾,建議使用絕對路徑。
1.3 AOF模式
1.3.1 模式說明
AOF模式可以做到實時數據備份,該模式相當於用配置文件記錄了用戶的全部操作過程。
特點:
- 可以實時備份數據,安全性好
- 持久化速度較RDB模式更慢
- AOF模式的持久化文件的體積會很大
- 當數據恢復時,需要消耗很長時間
- 持久化文件明文保存,沒有加密
1.3.2 AOF模式持久化策略
1.開啓AOF模式
注意:開啓AOF模式後,RDB模式不生效。
2.AOF模式持久化文件保存路徑
dir ./
3.持久化策略
# appendfsync always 每次操作都會備份
appendfsync everysec 每秒備份一次
# appendfsync no 不做操作
2 Redis內存策略
2.1 需求分析
question:
爲了保證服務器正常運行,需要爲redis設定最大內存空間,但是如果數據一直新增,內存很快就會佔滿,如何優化內存?
解決方案:
採用LRU算法,將內存中最近最不常用的數據清空,保存其他數據。
2.2 內存優化手段
1.設定內存空間
建議內存不要超過1G,通常256~512M足矣。
修改redis配置文件第537行
2.設定合適的內存策略
可選的內存策略:
-
volatile-lru
在已經設定超時時間的數據中,採用LRU算法進行刪除。 -
allkeys-lru
在所有key中,採用LRU算法進行刪除。 -
volatile-random
在已經設定超時時間的數據中,隨機刪除。 -
allkeys-radom
在所有key中,隨機刪除 -
volatile-ttl
在已經設定超時時間的數據中,根據TTL機制排序,將馬上要過期的數據刪除。 -
noviction
如果採用該機制,那麼內存數據不會刪除,將報錯消息返回給用戶。
策略設定:
2.3 緩存使用問題
1.緩存穿透
條件:訪問一條不存在的數據
說明:當訪問一個不存在的數據時,因爲緩存中沒有這個key,導致緩存形同虛設,最終訪問後臺數據庫,但數據庫中也沒有該數據所以返回null。
隱患:如果有人惡意頻繁查詢一個不存在的數據,可能會導致數據庫負載過高而宕機。
總結:業務系統訪問一個不存在的數據,稱之爲緩存穿透。
防護:限定同一IP訪問頻率,如京東。
2.緩存擊穿
條件:當一條緩存失效/過期/未命中時,高併發訪問該key
說明:如果給一個key設置了失效時間,當key失效時有一萬的併發請求訪問這個key,此時緩存失效,所有的請求都會訪問後臺數據庫,稱之爲緩存擊穿。
場景:微博熱點消息訪問量很大,如果該緩存失效則會直接訪問後臺數據庫,導致後臺數據庫負載過高。
3.緩存雪崩
前提:高併發訪問,緩存命中較低或者失效時。
說明:假設緩存都設置了失效時間,在同一時間內緩存大量失效,如果這時用戶高併發訪問,緩存命中率過低,導致全部的用戶訪問都會訪問後臺真實的數據庫。
場景:在高併發情況下,緩存動態更新時。
3 Redis分片技術
3.1 需求分析
1.如果將全部數據都保存到一臺tomcat中,如果該服務器損壞,則將影響全部服務。
2.使用單臺redis內存設定一般不要超過1G,但有些業務數據量很大,如果不修改內存則無法存儲。
3.2 改進方案
方案:採用Redis分片技術實現。
優點:
- 使用redis分片可以實現內存數據的動態擴容。
- 使用分片,每臺redis節點中儘可能存放1/n的數據,防止數據丟失。
- 對於用戶而言,整個redis分片就是一個服務。
3.3 分片搭建
3.3.1 複製配置文件
說明:將redis.conf文件複製到shards中,並且複製3份名稱爲:
- redis-6379.conf
- redis-6380.conf
- redis-6381.conf
3.3.2 修改端口號
說明:分別修改6380/6381的配置文件爲對應端口
3.3.3 重啓Redis服務
3.3.4 分片測試
@Test
public void testShard(){
/**
* 創建分片的對象
* 1.poolConfig 標識池的大小
* 2.shards redis分片的節點信息
*/
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(1000);
poolConfig.setTestOnBorrow(true); //獲取鏈接時先檢測
List<JedisShardInfo> shards = new ArrayList<>();
shards.add(new JedisShardInfo("192.168.126.166",6379));
shards.add(new JedisShardInfo("192.168.126.166",6380));
shards.add(new JedisShardInfo("192.168.126.166",6381));
ShardedJedisPool pool =
new ShardedJedisPool(poolConfig, shards);
//獲取redis鏈接
ShardedJedis jedis = pool.getResource();
jedis.set("shards","保存分片數據");
System.out.println(jedis.get("shards"));
//將鏈接還回池中
pool.returnResource(jedis);
}
3.4 Hash一致性算法
3.4.1 數據如何保存
1.利用節點的IP+算法確定唯一的哈希值,之後在內存中確定節點的位置。
2.當保存數據時,根據key進行hash運算,確定唯一的一個位置。
3.根據當前key位置順時針查找最近的node節點進行掛載。
3.4.2 均衡性
說明:根據redis節點的數量儘可能讓數據均勻地分片,每個節點中的數據儘可能趨向1/n。
提升:爲了保證均衡性,哈希一致性算法引入了虛擬節點的概念。爲了平衡數據,如果一個節點不夠則生成多個節點。
3.4.3 單調性
說明:如果node節點的新增,能動態地實現數據的遷移,這種特性被稱之爲單調性。
3.4.4 分散性
特點:由於分佈式的原因,用戶在使用內存時,不能夠獲取全部的內存空間,導致一個key對應多個位置。
3.4.5 負載
說明:負載是從另一個角度考慮分散性。
特點:一個位置對應多個key
如何解決:在進行架構設計時,要求使用全部的內存空間。
4 Spring整合分片
4.1 編輯pro文件
redis.host=192.168.126.166
redis.port.a=6379
redis.port.b=6380
redis.port.c=6381
redis.maxTotal=1000
4.2 編輯Spring配置文件
<!--實現spring整合分片 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${redis.maxTotal}"/>
</bean>
<!--定義三個redis節點 -->
<bean id="shardInfoA" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.a}"/>
</bean>
<bean id="shardInfoB" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.b}"/>
</bean>
<bean id="shardInfoC" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port.c}"/>
</bean>
<!--定義連接池 -->
<bean id="jedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg name="poolConfig" ref="poolConfig"/>
<constructor-arg name="shards">
<list>
<ref bean="shardInfoA"/>
<ref bean="shardInfoB"/>
<ref bean="shardInfoC"/>
</list>
</constructor-arg>
</bean>
4.3 編輯工具API
@Service
public class RedisService {
//有的工程需要,有的工程不需要。設置required=false,有就注入,沒有就不注入。
@Autowired(required = false)
private ShardedJedisPool shardedJedisPool;
public void set(String key,String value){
ShardedJedis jedis =
shardedJedisPool.getResource();
jedis.set(key, value);
shardedJedisPool.returnResource(jedis);
}
//添加超時時間
public void set(String key,String value,int seconds){
ShardedJedis jedis =
shardedJedisPool.getResource();
jedis.setex(key, seconds, value);
shardedJedisPool.returnResource(jedis);
}
//編輯get方法
public String get(String key){
ShardedJedis jedis =
shardedJedisPool.getResource();
String result = jedis.get(key);
shardedJedisPool.returnResource(jedis);
return result;
}
}
4.4 編輯業務層方法
1.修改自動注入對象
2.切換業務方法
5 Redis哨兵
5.1 業務需求
採用redis分片技術可以實現內存擴容,但是如果其中一臺機器宕機,則整個redis分片將不能正常運行。
5.2 實現主從掛載
角色劃分:
6379(主機)
6380(從機)
6381(從機)
1.檢查節點狀態
命令:
info replication
2.實現主從掛載
命令:
127.0.0.1:6380> SLAVEOF 192.168.126.166 6379
3.檢查狀態
5.3 哨兵實現
5.3.1 哨兵原理
- 哨兵監控主機的狀態,並獲取主機的全部信息,包括從機的信息。
- 通過心跳檢測機制,如果3次連接沒有響應,則斷定主機宕機了。此時哨兵會從從機中選舉一臺從機充當從機,同時將另外的從機掛載到現在的主機。
- 當舊的主機修復完成後,繼續充當從機,等待下次選舉。
5.3.2 編輯哨兵配置文件
1.以原名保存文件
cp sentinel.conf sentinel
2.關閉保護模式
3.修改哨兵監聽節點信息
sentinel master mymaster 127.0.0.1 1
說明:
mymaster 表示主機變量名稱
IP:端口 表示主機信息
2 表示選舉票數
4.修改推選時間
5.修改推選失敗時間
5.3.3 哨兵測試
1.啓動哨兵
redis-sentinel sentinel.conf
2.啓動測試
3.關閉主機,檢查哨兵能否推選