使用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
主從複製版
1主N從
這種模式簡單粗暴,但是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
樹狀主從(N級緩存)
從節點作爲主節點.
這種模式規避了單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), 該系統執行以下三個任務:
- 監控(Monitoring): Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。
- 提醒(Notification): 當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
- 自動故障遷移(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集羣
優點
- 支持Kubernetes
- 有web圖形界面,方便運維
- Redis獲得動態擴容/縮容的能力,增減redis實例對client完全透明、不需要重啓服務
缺點
- 穩定性堪憂
- 依賴於國內的豌豆莢團隊開發,迭代速度較慢
- 原版的docker鏡像較大,沒有根據組件進行分開
- 基於redis 3.x,而且很多原生的redis指令被閹割了
- 強依賴註冊中心(Zookeeper、Etcd、Fs)
我們用了幾個月吧,到後期頻繁出現
ERR handle response, backend conn reset
此外,日常觀察發現pod退出/重啓困難.如果某個group節點全部掛掉的話,整個集羣將不可讀寫.
綜上,codis已經影響到了嚴重影響到了我們程序的正確性,決定棄用codis.改爲普通的1主N從的模式.