Redis之集羣高可用與安全控制

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. 主從複製數據同步過程
  1. Redis從節點向主節點建立socket長連接,從服務器會發送一PING命令給主服務器;這時候PING命令可以檢查socket的讀寫狀態是否正常,還可以檢查主服務器能否正常處理命令請求。從服務器會向主服務器發送PSYNC命令,執行同步操作,並將自己的數據庫同步至主服務器數據庫當前的狀態。
  2. Redis通過心跳檢測機制定時向主服務器發送消息。
  3. Redis採用全量或者增量的形式將數據同步給從節點, 首次是全量,有數據更改是增量

***全量複製:***一般用於在初次的複製場景(從節點與主節點一次建立)

增量複製: 網絡出現問題,從節點再次連接主節點時,主節點補發缺少的數據,每次數據增量同步

1.5. 主從複製存在的缺陷

如果主節點存在了問題,整個Redis環境是不可以實現寫的操作,需要人工更改配置變爲主節點。

解決方案:使用哨兵機制可以幫助解決Redis集羣主從選舉策略。

哨兵機制只解決主從選舉策略,並不能解決同步的問題。

二、Redis哨兵機制

2.1. 哨兵機制

Redis的哨兵機制就是解決我們以上主從複製存在缺陷(選舉問題),解決問題保證我們的Redis高可用,實現自動化故障發現與故障轉移。

2.2. 哨兵機制原理
  1. 哨兵機制每個10s時間只需要配置監聽我們的主節點就可以獲取當前整個Redis集羣的環境列表,採用info 命令形式。
  2. 哨兵不建議是單機的,最好每個Redis節點都需要配置哨兵監聽。
  3. 哨兵集羣原理是如何:多個哨兵都執行同一個主的master節點,訂閱到相同都通道,有新的哨兵加入都會向通道中發送自己服務的信息,該通道的訂閱者可以發現新哨兵的加入,隨後相互建立長連接。
  4. 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失效的情況下

緩存服務器重啓或者大量的緩存集中在某個時間段失效,突然給數據庫產生了巨大的壓力,甚至擊垮數據庫的情況。

解決方案:

  • 對不用的數據使用不同的失效時間,加上隨機數。

四、上面的哨兵集羣已經上傳到碼雲大家可以自行下載實驗

https://gitee.com/molonglove/docker-es-service

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