程序猿必備的Redis常見功能知識點,這些你都會嗎?

源於螞蟻課堂的學習,點擊這裏查看(老餘很給力)    

基礎知識 

線程安全

由於單線程,故Redis天然規避線程安全問題。那麼,爲什麼單線程還能這麼高效呢?
這得益於其底層進行io操作時,採用了NIO的多路複用原則。(如有讀者感興趣,可以研究一下nio多路複用原理即可)

Redis官方是沒有windows版本的,因爲Redis底層做io操作是基於linux的epoll來進行NIO的io多路複用。
其主要通過socket收到消息後進行主動調用回調,去提升io輪詢效率。而Windows沒有epoll,只能通過selector去不停輪詢。

持久化機制

RDB

Redis默認採用rdb方式實現數據的持久化
即以快照的形式將數據持久化到磁盤,是一個二進制的文件dump.rdb

rdb方式會將Redis中的數據進行全量的備份,即覆蓋式更新,默認備份間隔時間是900s。
所以如果Redis出現故障無法備份數據時,如果採用rdb方式持久化數據,會導致這900s內數據的丟失。
但其優點是消耗服務器內存少

==================redis.conf==================
# rdb方式備份數據的文件名稱
dbfilename dump.rdb

# 在900秒(15分鐘)之後,如果至少有1個key發生變化,則dump內存快照
save 900 1

# 在300秒(5分鐘)之後,如果至少有10個key發生變化,則dump內存快照。
save 300 10

# 在60秒(1分鐘)之後,如果至少有10000個key發生變化,則dump內存快照。
save 60 10000

 

AOF  

AOF是基於數據日誌操作實現的持久化,故屬於增量備份
在Redis中,aof有三種模式

# 每次有數據修改發生時都會寫入AOF文件,能夠保證數據不丟失,但是效率非常低
appendfsync always

# 每秒鐘同步一次,可能會丟失1s內的數據,但是效率非常高, 這也是推薦使用的方式
appendfsync everysec

# 從不同步。高效但是數據不會被持久化。
appendfsync no

==================redis.conf================== 
# 開始aof持久化,默認採用everysec
appendonly yes

 

 與MySQL保持一致性

Redis作爲緩存,是需要和數據庫保持一致的,當然這裏的一致不是強一致,而是最終一致。

常見的解決方法有以下
1.通過mq去監聽MySQL的binlog文件,將其變化的結果以消息隊列的方式傳輸至redis
2.有種比較low的方式,就是手動清空redis,讓其訪問數據庫,然後同步至Redis,這種方式風險高,容易雪崩
3.可採用阿里的canal去實現數據同步

 

淘汰策略

Redis中的內存並非無止境,故當其內容滿了再去存放數據會出問題,所以其內部維護了6種淘汰策略

noeviction:不淘汰,當內存滿了,直接報錯
allkeys-lru:指定了有效期的key中,淘汰不經常使用的key
volatile-lru:指定了有效期的key中,隨機淘汰
allkeys-random:所有key中,淘汰不經常使用的key
volatile-random:所有key中,隨機淘汰
volatile-ttl:指定了有效期的key中,淘汰具有更早過期時間的key


==================redis.conf==================
# 標識Redis內存大小的閾值,超過則會進行淘汰策略
maxmemory <bytes>

# 指明Redis要使用的淘汰策略爲哪一種
maxmemory-policy 淘汰策略名稱

 

key的自動過期

當我們的key失效時,可以執行我們的客戶端回調監聽的方法

==================redis.conf==================
notify-keyspace-events "Ex"

SpringBoot整合key失效監聽

@Configuration
public class RedisListenerConfig {
    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        return container;
    }
}



@Component
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {
    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }


    /**
     * Redis失效事件 key
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiraKey = message.toString();
        // 拿到失效的key做一些業務處理
       
    }
}

 

事務 

multi 開啓事務
exec 提交事務
discard 取消提交事務
watch keyName 可以監聽一個或者多個key,提交事務前判斷key是否變化,進而進行事務提交或取消,通過版本實現樂觀鎖

ps:Redis官方是沒有提供回滾方法, 只提供了取消事務。
multi只保證了事務的原子性,但沒有保證其隔離性,故一般配合watch使用

 

分佈式鎖

分佈式鎖的大體實現思路是相同的,即多個請求去創建同一個節點,誰成功誰就拿到鎖。
Redis也是一樣的,它維護了一個setnx操作,即帶返回結果的設置key,1表示設置成功,0表示失敗

所以當setnx返回1時,執行業務操作,超時則回滾數據,正常執行結束後刪除此key,這樣其他請求繼續搶奪鎖。

 

 進階知識 

 主從複製

單個節點的Redis可用性不高,即當其宕機時,對外Redis不可用,故採用集羣的方式保證高可用。
主從模式遵循一主多從,讀寫分離(主寫從讀)。

==================redis.conf==================
# replicaof <masterip> <masterport>
slaveof 主節點的IP 主節點的端口
masterauth 主節點的密碼


從節點啓動時讀取其配置,根據主節點地址信息與其建立socket長連接,定期保持數據同步的通訊。
在一主多從模式中,如果一個主節點對應的從節點特別多時,主節點的同步壓力非常大,所以往往採用樹狀結構進行集羣。
即: master-->s0-->s2
                -->s3
           -->s1-->s4
           
從節點首次同步採用讀取rdb方式全量備份數據,當有更新key操作時,會採用增量方式同步

在客戶端連接中通過info replication可以獲取當前Redis節點集羣的信息

 

哨兵機制

主從模式存在很大的弊端,即當主節點宕機,會使得集羣對外不可寫,需要人工維護。
故Redis採用哨兵機制很好的解決這個問題。

哨兵是一個獨立的進程,其主要用於監聽master節點(定時向master發送ping的命令)的活躍狀態。
一般來說,一個Redis對應一個哨兵,多個哨兵都是監聽集羣中同一個master節點,當哨兵啓動時,會向消息隊列中訂閱
關於master的通道channel,然後將自身的地址作爲消息投放到消息通道中,然後作爲消費者等待消費,其它哨兵也做此
操作,這樣一來,哨兵之間可以得到彼此的信息,然後建立socket長連接進行通訊。
當有哨兵發現master宕機後,會發送消息至channel,通知所有哨兵去訪問master,超過閾值(可配置)數量的哨兵得
到master宕機結果的話,就會認爲master宕機,然後進行選舉。
之前的master啓動後,發現集羣已經有了master,那麼他就會變成slave的從。
通過這種機制,實現Redis的故障轉移


哨兵通過監聽master,執行info replication獲取master下對應從節點信息,然後向下遞歸執行info replication
從而獲取整個集羣的節點信息,進行選舉


==================sentinel.conf==================
sentinel monitor mymaster master的IP master的端口 哨兵閾值(認爲master宕機的閾值)
sentinel auth-pass mymaster master的密碼

# sentinel心跳檢測主3秒內無響應,視爲掛掉,開始切換其他從爲主
sentinel down-after-milliseconds mymaster 3000

# 每次最多可以有1個從同步主。一個從同步結束,另一個從開始同步
sentinel parallel-syncs mymaster 1

# 主從切換超時時間
sentinel failover-timeout mymaster 18000


#啓動哨兵
./redis-sentinel sentinel.conf的路徑

 

安全控制

緩存穿透 

大量Redis不存在的key訪問,導致Redis無法命中,感覺像是穿過了緩存,直接訪問數據庫,造成數據庫訪問壓力。

解決方法:
    api限流
    接口限制
    用戶授權
    IP檢查
    網關黑白名單
    布隆過濾器

 

緩存擊穿

在高併發的情況下,熱點key失效,導致大量請求訪問到了數據庫,如同某個點被擊穿一樣。

解決方法:
    分佈式鎖
    軟過期

緩存雪崩 

Redis持久化文件丟失,使得Redis啓動時進行數據預熱,大量的查詢湧向數據庫,導致數據庫無法承受而宕機。
這樣一來,緩存拿不到數據,數據庫又起不來,形成雪崩效應

Cluster集羣

傳統的主從模式有很大弊端,即:
    中心化 只有一個主在寫,宕機需要選舉,期間對外不可用
    數據全量同步 佔用資源,內容冗餘

故Redis推出分片化管理數據
    其原理是內部維護一個hash槽,默認槽的個數是16384個,然後支持多個master節點,每個master可以支持各自的主從。
    
    根據槽的長度取模master節點數,將槽的下標進行均勻分配,即每個master都會有屬於自己的槽區間
    通過對key進行crc16算法計算及其然後對16384取模獲取此key對應的槽下標
    根據槽下標尋找相應的master節點,然後將其存放至對應的槽位,一個槽位類似於一張表,即可存放多個key
    
    其原理近似MySQL的分庫分表和hashMap
    

RedisCluster集羣模式環境搭建

mkdir rediscluster
cd rediscluster/
mkdir redis7000
mkdir redis7001
mkdir redis7002
mkdir redis7003
mkdir redis7004
mkdir redis7005


每個配置文件內容(以7005爲例)
# 後臺啓動
daemonize yes 
# 允許外部訪問
protected-mode no 
# 修改端口號,從7000到7005
port 7005 
# 開啓cluster,去掉註釋
cluster-enabled yes 
# 自動生成
cluster-config-file 7000nodes.conf
# 節點通信時間 
cluster-node-timeout 15000 
logfile   /usr/rediscluster/redis7005/redis.log


啓動我們的redis

/usr/redis/bin/redis-server /usr/rediscluster/redis7000/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7001/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7002/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7003/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7004/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7005/redis.conf


連接任意一個redis
/usr/redis/bin/redis-cli -h 192.168.212.163 -p 7000

(error) CLUSTERDOWN Hash slot not served  說明沒有分配hash槽

# 分配hash槽
終端執行
/usr/redis/bin/redis-cli --cluster create  192.168.212.163:7000  192.168.212.163:7001  192.168.212.163:7002  192.168.212.163:7003  192.168.212.163:7004  192.168.212.163:7005  --cluster-replicas 1
(建議最好使用服務器的ip地址搭建)

# -c 標識當key的槽下標不在此Redis時,會進行重定向
/usr/redis/bin/redis-cli -h 192.168.212.163 -p 7000 –c

 

動態擴容

擴容節點
/usr/redis/bin/redis-server /usr/rediscluster/redis7006/redis.conf
/usr/redis/bin/redis-server /usr/rediscluster/redis7007/redis.conf


新增一個主節點 爲7006
/usr/redis/bin/redis-cli --cluster add-node 192.168.212.163:7006   192.168.212.163:7000

新增一個從節點 爲7007
/usr/redis/bin/redis-cli --cluster add-node 192.168.212.163:7007   192.168.212.163:7000  --cluster-salve  --cluster-master-id     5d94171eb34ed4396bf5b9db8efaab4d96d0cf10(7006的ID)


新增的7006 是沒有任何槽位 需分配Redis槽位擴容
cluster slots 
/usr/redis/bin/redis-cli --cluster reshard  192.168.212.163:7000

 

動態縮容

/usr/redis/bin/redis-cli --cluster  reshard  192.168.212.163:7000  --cluster-from   5d94171eb34ed4396bf5b9db8efaab4d96d0cf10  --cluster-to 511058958a3b80dd600e060c2500050c6c5a02ab  --cluster-slots 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章