天天看點

分析redis的各種使用情景

使用docker-compose示範redis的各種使用情景,最後介紹了

codis

kubernetes

方案

總結

模式 特點
單機版-RDB 備份快,但資料可能不完整
單機版-AOF 備份慢,但是資料比較完整
1主N從 讀寫分離的典範
樹狀主從(N級緩存) 為了規避主重新開機導緻的大規模全量複制,但是需要維持每一個中間master的健康
主從自動切換(Sentinel) 在主從的基礎上加了Sentinel角色,通過Sentinel實作主從的自動切換
叢集(N主N從) 基于slot的key分片,用戶端支援得不是很多,是以用的人不多

單機版

RDB模式(預設模式)

定期快照模式

version: '3'
services:   
    redis-master: 
        image: redis
        ports: 
        - "12660:6379"
        expose:
          - "6379"
        networks:
        - default        
        entrypoint:
        - redis-server
        volumes:
          - ./data:/data           

AOF模式

逐一寫入,資料比較完整,檔案較大,但恢複較慢

version: '3'
services:   
    redis-master: 
        image: redis
        ports: 
        - "12660:6379"
        expose:
          - "6379"
        networks:
        - default        
        entrypoint:
        - redis-server 
        - --appendonly yes
        volumes:
          - ./data:/data           

主從複制版

這種模式簡單粗暴,但是master一旦重新開機,多從節點全量複制,IO将會比較繁重

version: '3'
services:   
    redis-master: 
        image: redis
        ports: 
        - "12660:6379"
        expose:
          - "6379"
        networks:
        - default        
        entrypoint:
        - redis-server 
        - --save 1 1
        volumes:
          - ./data:/data
    redis-slave1: 
        image: redis
        ports: 
        - "12661:6379"
        expose:
          - "6379"        
        networks:
        - default             
        entrypoint:
        - redis-server 
        - --slaveof redis-master 6379
        - --slave-serve-stale-data yes
        #當從機與主機斷開連接配接時,或者當複制仍在進行時,slave仍然會回複client請求, 盡管資料可能會出現過期或者如果這是第一次同步,資料集可能為空。
        - --slave-read-only yes
        #0作為一個特殊的優先級,辨別這個slave不能作為master 
        - --slave-priority 100
    redis-slave2: 
        image: redis
        ports: 
        - "12662:6379"
        expose:
          - "6379"            
        entrypoint:
        - redis-server         
        - --slaveof redis-master 6379
        - --slave-serve-stale-data yes
        #當從機與主機斷開連接配接時,或者當複制仍在進行時,slave仍然會回複client請求, 盡管資料可能會出現過期或者如果這是第一次同步,資料集可能為空。
        - --slave-read-only yes
        #0作為一個特殊的優先級,辨別這個slave不能作為master 
        - --slave-priority 100           

從節點作為主節點.

這種模式規避了單master

version: '3'
services:   
    redis-master: 
        image: redis
        ports: 
        - "12660:6379"
        expose:
          - "6379"
        networks:
        - default        
        entrypoint:
        - redis-server 
        - --save 1 1
        volumes:
          - ./data:/data
    redis-slave1: 
        image: redis
        ports: 
        - "12661:6379"
        expose:
          - "6379"        
        networks:
        - default             
        entrypoint:
        - redis-server 
        - --slaveof redis-master 6379
        - --slave-serve-stale-data yes
        #當從機與主機斷開連接配接時,或者當複制仍在進行時,slave仍然會回複client請求, 盡管資料可能會出現過期或者如果這是第一次同步,資料集可能為空。
        - --slave-read-only yes
        #0作為一個特殊的優先級,辨別這個slave不能作為master 
        - --slave-priority 100
        # - --masterauth xxx
    redis-slave2: 
        image: redis
        ports: 
        - "12662:6379"
        expose:
          - "6379"            
        entrypoint:
        - redis-server         
        - --slaveof redis-slave1 6379
        - --slave-serve-stale-data yes
        #當從機與主機斷開連接配接時,或者當複制仍在進行時,slave仍然會回複client請求, 盡管資料可能會出現過期或者如果這是第一次同步,資料集可能為空。
        - --slave-read-only yes
        #0作為一個特殊的優先級,辨別這個slave不能作為master 
        - --slave-priority 100
        # - --masterauth xxx           

主從自動切換(Sentinel模式)

這時需要引入 Sentinel 的概念

Redis 的 Sentinel 系統用于管理多個 Redis 伺服器(instance), 該系統執行以下三個任務:

  1. 監控(Monitoring): Sentinel 會不斷地檢查你的主伺服器和從伺服器是否運作正常。
  2. 提醒(Notification): 當被監控的某個 Redis 伺服器出現問題時, Sentinel 可以通過 API 向管理者或者其他應用程式發送通知。
  3. 自動故障遷移(Automatic failover): 當一個主伺服器不能正常工作時, Sentinel 會開始一次自動故障遷移操作, 它會将失效主伺服器的其中一個從伺服器更新為新的主伺服器, 并讓失效主伺服器的其他從伺服器改為複制新的主伺服器; 當用戶端試圖連接配接失效的主伺服器時, 叢集也會向用戶端傳回新主伺服器的位址, 使得叢集可以使用新主伺服器代替失效伺服器。
預設的Sentinel配置

去掉注釋後長這樣

port 26379
daemonize no
pidfile /var/run/redis-sentinel.pid
logfile ""
dir /tmp
# 監視主伺服器,下線master需要2個Sentinel同意
sentinel monitor mymaster redis-master 6379 2
# 30秒内有效回複視為master健康,否則下線
sentinel down-after-milliseconds mymaster 30000
# 故障轉移時,從伺服器同部數最大值
sentinel parallel-syncs mymaster 1
# 1. 同一個sentinel對同一個master兩次failover之間的間隔時間。
# 2. 當一個slave從一個錯誤的master那裡同步資料開始計算時間。直到slave被糾正為向正确的master那裡同步資料時。
# 3.當想要取消一個正在進行的failover所需要的時間。  
# 4.當進行failover時,配置所有slaves指向新的master所需的最大時間。不過,即使過了這個逾時,slaves依然會被正确配置為指向master,但是就不按parallel-syncs所配置的規則來了。
sentinel failover-timeout mymaster 90000           

基本格式是

sentinel <選項的名字> <主伺服器的名字> <選項的值>

原本我想通過

docker-compose up --scale redis-sentinel=3

直接啟動3個容器,結果發現這容器竟然會修改配置檔案,是以隻能分開寫了

初版

# docker-compose up --scale redis-sentinel=3 雖然可以啟動,但是3個容器同時寫同一個檔案,感覺不是很好
version: '3'
services:   
    redis-master: 
        image: redis
        ports: 
        - "12660:6379"
        expose:
          - "6379"  
        entrypoint:
        - redis-server 
        - --save 1 1
        volumes:
          - ./data:/data
        # 重新開機政策改為no手動讓他當機模拟主從切換
        restart: "no"
    redis-slave1: 
        image: redis
        ports: 
        - "12661:6379"
        expose:
          - "6379"          
        entrypoint:
        - redis-server 
        - --slaveof redis-master 6379
        - --slave-serve-stale-data yes
        #當從機與主機斷開連接配接時,或者當複制仍在進行時,slave仍然會回複client請求, 盡管資料可能會出現過期或者如果這是第一次同步,資料集可能為空。
        - --slave-read-only yes
        #0作為一個特殊的優先級,辨別這個slave不能作為master 
        - --slave-priority 100
        # - --masterauth xxx
    redis-slave2: 
        image: redis
        ports: 
        - "12662:6379"
        expose:
          - "6379"            
        entrypoint:
        - redis-server         
        - --slaveof redis-slave1 6379
        - --slave-serve-stale-data yes
        #當從機與主機斷開連接配接時,或者當複制仍在進行時,slave仍然會回複client請求, 盡管資料可能會出現過期或者如果這是第一次同步,資料集可能為空。
        - --slave-read-only yes
        #0作為一個特殊的優先級,辨別這個slave不能作為master 
        - --slave-priority 100
        # - --masterauth xxx
    redis-sentinel:
        image: redis
        expose:
          - "26379"
        entrypoint:
        - redis-server
        - /usr/local/etc/redis/redis.conf
        - --sentinel
        volumes:
        - ./redis-sentinel.conf:/usr/local/etc/redis/redis.conf           

最終版:

version: '3'
services:   
    redis-master: 
        image: redis
        ports: 
        - "12660:6379"
        expose:
          - "6379"  
        entrypoint:
        - redis-server 
        - --save 1 1
        volumes:
          - ./data:/data
        # 重新開機政策改為no手動讓他當機模拟主從切換
        restart: "no"
    redis-slave1: 
        image: redis
        ports: 
        - "12661:6379"
        expose:
          - "6379"          
        entrypoint:
        - redis-server 
        - --slaveof redis-master 6379
        - --slave-serve-stale-data yes
        #當從機與主機斷開連接配接時,或者當複制仍在進行時,slave仍然會回複client請求, 盡管資料可能會出現過期或者如果這是第一次同步,資料集可能為空。
        - --slave-read-only yes
        #0作為一個特殊的優先級,辨別這個slave不能作為master 
        - --slave-priority 100
        # - --masterauth xxx
    redis-slave2: 
        image: redis
        ports: 
        - "12662:6379"
        expose:
          - "6379"            
        entrypoint:
        - redis-server         
        - --slaveof redis-slave1 6379
        - --slave-serve-stale-data yes
        #當從機與主機斷開連接配接時,或者當複制仍在進行時,slave仍然會回複client請求, 盡管資料可能會出現過期或者如果這是第一次同步,資料集可能為空。
        - --slave-read-only yes
        #0作為一個特殊的優先級,辨別這個slave不能作為master 
        - --slave-priority 100
        # - --masterauth xxx
    redis-sentinel1:
        image: redis
        expose:
          - "26379"
        entrypoint:
        - redis-server
        - /usr/local/etc/redis/redis.conf
        - --sentinel
        volumes:
        - ./redis-sentinel1.conf:/usr/local/etc/redis/redis.conf
    redis-sentinel2:
        image: redis
        expose:
          - "26379"
        entrypoint:
        - redis-server
        - /usr/local/etc/redis/redis.conf
        - --sentinel
        volumes:
        - ./redis-sentinel2.conf:/usr/local/etc/redis/redis.conf
    redis-sentinel3:
        image: redis
        expose:
          - "26379"
        entrypoint:
        - redis-server
        - /usr/local/etc/redis/redis.conf
        - --sentinel
        volumes:
        - ./redis-sentinel3.conf:/usr/local/etc/redis/redis.conf           

sentinel啟動之後,配置發生了變化

這些内容沒了

sentinel monitor mymaster redis-master 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1           

變成

sentinel myid 3ae98d6815c1a9b941f8283b7e48bfeef7435905
sentinel deny-scripts-reconfig yes
# Generated by CONFIG REWRITE
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0
sentinel known-replica mymaster 172.24.0.4 6379
sentinel known-sentinel mymaster 172.24.0.7 26379 19ac7e0519e3c30a75e23bac34a7033594256c54
sentinel known-sentinel mymaster 172.24.0.5 26379 c596734a7f55ba2b6c7e3e81562aa6687e45fdeb
sentinel current-epoch 0           

之後通過

docker ps

docker stop

手動停掉了master那個容器,sentinel發覺了,并重新選主.

此時通過

redis-cli

輸入info,發現它已經成功變成了可以寫入資料的

master

# Generated by CONFIG REWRITE
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-replica mymaster 172.24.0.3 6379
sentinel known-replica mymaster 172.24.0.2 6379
sentinel known-sentinel mymaster 172.24.0.7 26379 19ac7e0519e3c30a75e23bac34a7033594256c54
sentinel known-sentinel mymaster 172.24.0.5 26379 c596734a7f55ba2b6c7e3e81562aa6687e45fdeb
sentinel current-epoch 1           

此時重新開機master,雖然他以server形式啟動,但是角色已經自動被貶為slave.

此時master沒有變化,是以sentinel的配置内容沒有變

叢集版(N主N從)

叢集基于16384個slot做分片.目前各語言用戶端實作比較少.是以用的人不是很多.

在docker中運作時,需要使用host網絡模式(--net=host)

redis-trib.rb

redis版本<5時,可以用

redis-trib.rb

建叢集

./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

# 引入新節點
./redis-trib.rb add-node 127.0.0.1:7006 <任意節點IP>:<節點端口>

# 重新分片
./redis-trib.rb reshard <任意節點IP>:<節點端口>
           

redis-cli

=5直接用redis-cli即可.
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \
127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1
redis-cli --cluster reshard 127.0.0.1:7000
redis-cli reshard <host>:<port> --cluster-from <node-id> --cluster-to <node-id> --cluster-slots <number of slots> --cluster-yes
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
# Adding a new node as a replica
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 --cluster-slave --cluster-master-id 3c3a0c74aae0b56170ccb03a76b60cfe7dc1912e
redis-cli --cluster del-node 127.0.0.1:7000 `<node-id>`
           

叢集指令

CLUSTER REPLICATE <master-node-id>
cluster nodes           

Kubernetes

個人覺得吧,redis跟Kubernetes不是特别契合.kubernetes本身有網絡瓶頸的問題,通過svc去通路,頻繁DNS解析也不好對吧.這對于高頻通路redis的場景來說是緻命的.

而且pod這種易失架構注定了在Kubernetes上面用redis要麼用資料卷挂載,要麼用主從自動切換模式(純記憶體的話至少要1主2從,并且用反親和度錯開彼此的運作節點).

主從和單機版倒好解決,單機的話挂載好資料卷,主從的話,主和從分開2個deploy/statefulset部署即可.

但是叢集版就比較麻煩.官方的設計還是偏向于傳統二進制人工運維,沒有做到雲原生

看了一下官方的helm chart,也是用的主從模式.

codis

codis是redis叢集沒出來之前,豌豆莢團隊做的一個方案,通過proxy,隔離了後端的redis叢集

優點

  1. 支援Kubernetes
  2. 有web圖形界面,友善運維
  3. Redis獲得動态擴容/縮容的能力,增減redis執行個體對client完全透明、不需要重新開機服務

缺點

  1. 穩定性堪憂
  2. 依賴于國内的豌豆莢團隊開發,疊代速度較慢
  3. 原版的docker鏡像較大,沒有根據元件進行分開
  4. 基于redis 3.x,而且很多原生的redis指令被閹割了
  5. 強依賴注冊中心(Zookeeper、Etcd、Fs)

我們用了幾個月吧,到後期頻繁出現

ERR handle response, backend conn reset           

此外,日常觀察發現pod退出/重新開機困難.如果某個group節點全部挂掉的話,整個叢集将不可讀寫.

綜上,codis已經影響到了嚴重影響到了我們程式的正确性,決定棄用codis.改為普通的1主N從的模式.

主挂了之後導緻服務不可用 #1356

參考連結

  1. 【Redis筆記】 第4篇: redis.conf中Replication配置項說明
  2. Redis 配置檔案詳解
  3. redis指令參考
  4. redis主從複制常見的一些坑
  5. Redis 的各項功能解決了哪些問題?
  6. 叢集教程

繼續閱讀