Redis
之集羣高可用和安全控制
一、Redis
的主從複製
1.1. 爲什麼使用主從
單個Redis
如果因爲某種原因宕機的話,可能會導致Redis
服務不可用,可以使用主從複製實現一主多從,主節點負責寫的操作,從節點負責讀的操作,主節點會定期將數據同步到從節點中,保證數據一致性的問題。
1.2. 主從的方式
最少需要3個節點。
第一種:
第二種:
優先選擇第二種,第一種方式主節點向從節點同步數據壓力大。
1.3. 主從複製配置:
將編譯之後的Redis
中的bin
目錄中全部內容
角色 | 端口 | 配置文件 |
---|---|---|
主節點 | 6379 | redis_6379.conf |
從節點 | 6380 | redis_6380.conf |
從節點 | 6381 | redis_6381.conf |
從節點 | 6382 | redis_6382.conf |
相關目錄結構
內存有限,我們只模擬不同端口下的Redis
主從複製。我模擬了一主三從,採用樹狀結構:
核心配置:主節點
# ip監控
bind 0.0.0.0
protected-mode no
# requirepass 123456
# 端口
port 6379
# 後臺運行
daemonize yes
# 工作目錄
dir ./
# pid
pidfile "/var/run/redis_6379.pid"
# 日誌名
logfile "redis_6379.log"
# RDB同步數據
dbfilename "dump_6379.rdb"
# AOF同步
appendonly yes
appendfilename "appendonly_6379.aof"
# AOF 策略
appendfsync everysec
# 失效消息通知配置
notify-keyspace-events "xE"
從節點:其他的從節點和這個一樣
# ip監控
bind 0.0.0.0
protected-mode no
# requirepass 123456
# 端口
port 6379
# 後臺運行
daemonize yes
# 工作目錄
dir ./
# pid redis_6380 對應的端口號,自己替換
pidfile "/var/run/redis_6380.pid"
# 日誌名
logfile "redis_6380.log"
# RDB同步數據
dbfilename "dump_6380.rdb"
# AOF同步
appendonly yes
appendfilename "appendonly_6380.aof"
# AOF 策略
appendfsync everysec
notify-keyspace-events "xE"
# slaveof和replicaof一樣效果
replicaof 192.168.252.131 6379
Redis
命令添加從節點:重啓之後失效
127.0.0.1:6380> slaveof 192.168.252.131 6379
ok
結構圖:
查看進程:
[root@long-test redis-slave-3]# ps -ef | grep redis | grep -v grep
root 12636 1 0 09:02 ? 00:00:01 ./bin/redis-server 0.0.0.0:6379
root 12685 1 0 09:12 ? 00:00:00 ./bin/redis-server 0.0.0.0:6380
root 12700 1 0 09:15 ? 00:00:00 ./bin/redis-server 0.0.0.0:6381
root 12716 1 0 09:19 ? 00:00:00 ./bin/redis-server 0.0.0.0:6382
root 12751 12505 0 09:21 pts/1 00:00:00 redis-cli -p 6382
[root@long-test redis-slave-3]#
主從節點詳細信息:
127.0.0.1:6379> info replication # 主節點 6379
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.252.131,port=6380,state=online,offset=1427,lag=0 # 從節點 6380
master_replid:62e34afa2f9e2b2fdf23614109aaf63b5d45c380
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1427
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1427
# ------------------------------------------------------------------------------
127.0.0.1:6380> info replication # 從節點6380, 有兩個從節點通過6380連接進行同步
# Replication
role:slave
master_host:192.168.252.131 # 主節點 6379
master_port:6379
master_link_status:up
master_last_io_seconds_ago:7
master_sync_in_progress:0
slave_repl_offset:1245
slave_priority:100
slave_read_only:1
connected_slaves:2
slave0:ip=192.168.252.131,port=6381,state=online,offset=1245,lag=0 # 6381節點
slave1:ip=192.168.252.131,port=6382,state=online,offset=1245,lag=0 # 6382節點
master_replid:62e34afa2f9e2b2fdf23614109aaf63b5d45c380
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1245
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1245
測試:
[root@long-test bin]# redis-cli # 主節點6379
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> set name tom # 可讀可寫
OK
127.0.0.1:6379> get name
"tom"
127.0.0.1:6379> exit
[root@long-test bin]# redis-cli -p 6380 # 從節點 6380
127.0.0.1:6380> auth 123456
OK
127.0.0.1:6380> get name # 獲取到剛纔的key
"tom"
127.0.0.1:6380> exit
[root@long-test bin]# redis-cli -p 6381 # 從節點 6381
127.0.0.1:6381> auth 123456
OK
127.0.0.1:6381> get name
"tom"
127.0.0.1:6381> exit
[root@long-test bin]# redis-cli -p 6382 # 從節點 6382
127.0.0.1:6382> auth 123456
OK
127.0.0.1:6382> get name
"tom"
127.0.0.1:6382> set age 123 # 不可寫,只能讀
(error) READONLY You can't write against a read only replica.
127.0.0.1:6382>
1.4. 主從複製數據同步過程
Redis
從節點向主節點建立socket
長連接,從服務器會發送一PING命令給主服務器;這時候PING命令可以檢查socket的讀寫狀態是否正常,還可以檢查主服務器能否正常處理命令請求。從服務器會向主服務器發送PSYNC
命令,執行同步操作,並將自己的數據庫同步至主服務器數據庫當前的狀態。Redis
通過心跳檢測機制定時向主服務器發送消息。Redis
採用全量或者增量的形式將數據同步給從節點, 首次是全量,有數據更改是增量
***全量複製:***一般用於在初次的複製場景(從節點與主節點一次建立)
增量複製: 網絡出現問題,從節點再次連接主節點時,主節點補發缺少的數據,每次數據增量同步
1.5. 主從複製存在的缺陷
如果主節點存在了問題,整個Redis
環境是不可以實現寫的操作,需要人工更改配置變爲主節點。
解決方案:使用哨兵機制可以幫助解決Redis
集羣主從選舉策略。
哨兵機制只解決主從選舉策略,並不能解決同步的問題。
二、Redis
哨兵機制
2.1. 哨兵機制
Redis
的哨兵機制就是解決我們以上主從複製存在缺陷(選舉問題),解決問題保證我們的Redis
高可用,實現自動化故障發現與故障轉移。
2.2. 哨兵機制原理
- 哨兵機制每個10s時間只需要配置監聽我們的主節點就可以獲取當前整個
Redis
集羣的環境列表,採用info 命令形式。 - 哨兵不建議是單機的,最好每個
Redis
節點都需要配置哨兵監聽。 - 哨兵集羣原理是如何:多個哨兵都執行同一個主的
master
節點,訂閱到相同都通道,有新的哨兵加入都會向通道中發送自己服務的信息,該通道的訂閱者可以發現新哨兵的加入,隨後相互建立長連接。 Master
的故障發現 單個哨兵會向主的master節點發送ping的命令,如果master節點沒有及時的響應,哨兵會認爲該master節點爲“主觀不可用狀態”會發送給其他都哨兵確認該Master節點是否不可用,當前確認的哨兵節點數>=quorum(可配置),會實現重新選舉。
2.3. 原理圖
2.4. 核心配置
我們接着上面的主從配置,接着測試哨兵集羣的主節點宕機之後自動選舉產生新的主節點。
節點 | 端口 | 配置文件 |
---|---|---|
主節點 | 26379 | sentinel_26379.conf |
從節點 | 26380 | sentinel_26380.conf |
從節點 | 26381 | sentinel_26381.conf |
從節點 | 26382 | sentinel_26382.conf |
哨兵節點配置:需要和現有的Redis
服務器數量相同。
注意 一個哨兵節點也是可以實現故障轉移的,配置哨兵和節點數據一致,爲了高可用。
bind 0.0.0.0
# 端口
port 26379
# 後臺
daemonize yes
# 工作目錄
dir ./
# 日誌文件 sentinel_26379 對應不同的端口 ,自己配置
logfile "sentinel_26379.log"
# 監控的主節點
protected-mode no
# 監聽主節點 ip和端口 3 是主節點掛掉之後需要幾個節點確認之後就可以選舉主節點
sentinel monitor mymaster 192.168.252.131 6380 3
啓動哨兵:
# 其他的redis哨兵啓動一樣,對應不同的端口哨兵配置文件
[root@long-test redis-master]# ./bin/redis-sentinel sentinel_26379.conf
查看進程:
[root@long-test redis-master]# ps -ef | grep redis
root 15252 1 0 20:25 ? 00:00:06 ./bin/redis-sentinel 0.0.0.0:26379 [sentinel]
root 15257 1 0 20:26 ? 00:00:03 ./bin/redis-server 0.0.0.0:6380
root 15264 1 0 20:26 ? 00:00:05 ./bin/redis-sentinel 0.0.0.0:26380 [sentinel]
root 15269 1 0 20:26 ? 00:00:03 ./bin/redis-server 0.0.0.0:6381
root 15276 1 0 20:26 ? 00:00:05 ./bin/redis-sentinel 0.0.0.0:26381 [sentinel]
root 15281 1 0 20:26 ? 00:00:03 ./bin/redis-server 0.0.0.0:6382
root 15288 1 0 20:27 ? 00:00:05 ./bin/redis-sentinel 0.0.0.0:26382 [sentinel]
root 15387 1 0 20:56 ? 00:00:01 ./bin/redis-server 0.0.0.0:6379
root 15464 14823 0 21:20 pts/0 00:00:00 grep --color=auto redis
[root@long-test redis-master]#
這啓動之後就會發現,哨兵文件會出現如下內容:
[root@long-test redis-master]# cat sentinel_26379.conf
bind 0.0.0.0
# 端口
port 26379
# 後臺
daemonize yes
# 工作目錄
dir "/opt/redis-cluster-service/redis-master"
# 日誌文件
logfile "sentinel_26379.log"
# 監控的主節點
# Generated by CONFIG REWRITE 下面啓動之後添加的
protected-mode no
sentinel myid 2308a50d7354fab01bb1710b658b6beb6fd9e029
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 192.168.252.131 6379 3
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
# 從節點
sentinel known-replica mymaster 192.168.252.131 6380
sentinel known-replica mymaster 192.168.252.131 6381
sentinel known-replica mymaster 192.168.252.131 6382
# 哨兵節點
sentinel known-sentinel mymaster 192.168.252.131 26381 23c43bfdc86a95f4a0c1504c229250bc821772f8
sentinel known-sentinel mymaster 192.168.252.131 26380 7b32e61d00c9d7a47adb92142615ea94b39fbc59
sentinel known-sentinel mymaster 192.168.252.131 26382 72848de80af7f13209d24d7e683a07f9f25816f5
sentinel current-epoch 1
關閉主節點:
[root@long-test redis-master]# redis-cli -p 6379
127.0.0.1:6379> shutdown # 關閉redis
查看主節點的配置文件:
[root@long-test redis-slave-1]# cat redis_6379.conf
# ip監控
bind 0.0.0.0
protected-mode no
# requirepass 123456
# 端口
port 6379
# 後臺運行
daemonize yes
# 工作目錄
dir "/opt/redis-cluster-service/redis-slave-1"
# pid
pidfile "/var/run/redis_6379.pid"
# 日誌名
logfile "redis_6379.log"
# RDB同步數據
dbfilename "dump_6379.rdb"
# AOF同步
appendonly yes
appendfilename "appendonly_6379.aof"
# AOF 策略
appendfsync everysec
notify-keyspace-events "xE"
# 標註所屬的主機
# 這個是哨兵幫我們刪除的
# Generated by CONFIG REWRITE
哨兵切換主節點:
127.0.0.1:6379> shutdown # 關閉6379端口對應的redis服務器
not connected> exit
[root@long-test redis-slave-1]# redis-cli -p 26381 # 進入26381端口的哨兵
127.0.0.1:26381> info sentinel # 查看哨兵已經將主節點切換到了6381端口對應的節點了
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=192.168.252.131:6381,slaves=3,sentinels=4
127.0.0.1:26381>
開啓剛纔關閉的節點:查看redis的配置文件
[root@long-test redis-master]# cat redis_6379.conf
# ip監控
bind 0.0.0.0
protected-mode no
# requirepass 123456
# 端口
port 6379
# 後臺運行
daemonize yes
# 工作目錄
dir "/opt/redis-cluster-service/redis-slave-1"
# pid
pidfile "/var/run/redis_6379.pid"
# 日誌名
logfile "redis_6379.log"
# RDB同步數據
dbfilename "dump_6379.rdb"
# AOF同步
appendonly yes
appendfilename "appendonly_6379.aof"
# AOF 策略
appendfsync everysec
notify-keyspace-events "xE"
# 標註所屬的主機
# 這個是哨兵幫我們添加的新主節點
# Generated by CONFIG REWRITE
replicaof 192.168.252.131 6381
三、Redis
安全控制
3.1. 緩衝穿透:key不存在的情況
key
對應的數據在數據源並不存在,每次針對此key
的請求從緩存獲取不到,請求都會到數據源,從而可能壓垮數據源。比如用一個不存在的用戶id獲取用戶信息,不論緩存還是數據庫都沒有,若黑客利用此漏洞進行攻擊可能壓垮數據庫。
解決方案:
- 接口層實現
api
限流、用戶授權、id檢測 - 從緩衝和數據庫都獲取不到數據,一樣可以將數據庫空值放入緩衝中,設置30s有效期避免使用同一個id對數據庫攻擊壓力大
3.2. 緩衝擊穿:單個熱點key
失效在併發查詢的情況下
在高併發的情況下,當一個緩存key過期時,因爲訪問該key請求較大,多個請求同時發現緩存過期,因此對多個請求同時數據庫查詢、同時向Redis寫入緩存數據,這樣會導致數據庫的壓力非常大。
解決方案:
- 使用分佈式鎖:保證在分佈式情況下,使用分佈式鎖保證對每一個
key
同時只允許一個線程查詢到後端服務,其他沒有獲取到鎖的權限,只需要等待即可,這種高併發壓力直接轉移到分佈式鎖上,對分佈式鎖壓力非常大。 - 使用本地鎖:使用本地鎖與分佈式機制一樣,只不過分佈式鎖適應於集羣;本地鎖僅限於單個服務使用。
- 軟過期:設置熱點數據永不過期或者異步延長過期時間
- 布隆過濾器
3.3. 緩衝雪崩:多個key
失效的情況下
緩存服務器重啓或者大量的緩存集中在某個時間段失效,突然給數據庫產生了巨大的壓力,甚至擊垮數據庫的情況。
解決方案:
- 對不用的數據使用不同的失效時間,加上隨機數。
四、上面的哨兵集羣已經上傳到碼雲大家可以自行下載實驗
https://gitee.com/molonglove/docker-es-service
致數據庫的壓力非常大。
解決方案:
- 使用分佈式鎖:保證在分佈式情況下,使用分佈式鎖保證對每一個
key
同時只允許一個線程查詢到後端服務,其他沒有獲取到鎖的權限,只需要等待即可,這種高併發壓力直接轉移到分佈式鎖上,對分佈式鎖壓力非常大。 - 使用本地鎖:使用本地鎖與分佈式機制一樣,只不過分佈式鎖適應於集羣;本地鎖僅限於單個服務使用。
- 軟過期:設置熱點數據永不過期或者異步延長過期時間
- 布隆過濾器
3.3. 緩衝雪崩:多個key
失效的情況下
緩存服務器重啓或者大量的緩存集中在某個時間段失效,突然給數據庫產生了巨大的壓力,甚至擊垮數據庫的情況。
解決方案:
- 對不用的數據使用不同的失效時間,加上隨機數。