Redis 实战之主从复制、高可用、分布式
簡介
本節(jié)內容基于 CentOS 7.4.1708,Redis 3.2.12 環(huán)境實驗。
Redis 是一個開源的高性能鍵值對數(shù)據(jù)庫。
安裝:yum install -y redis
特性:
高性能 Key-Value 服務器
多種數(shù)據(jù)結構
豐富功能
緩存(get|set)
計數(shù)器(incre)
消息隊列(publish|subcrib)
高可用(v2.8 redis-sentinel)
分布式(v3.0 redis-cluster)
可執(zhí)行文件:
redis-server:服務端
redis-cli:客戶端
redis-benchmark:性能測試工具
redis-check-aof:aof修復工具
redis-check-dump:rdb修復工具
redis-sentinel:sentinel服務端
啟動方式:
最簡啟動:默認配置直接啟動redis-server
動態(tài)參數(shù)啟動:命令行指定配置啟動redis-server
配置文件啟動(推薦):指定配置文件啟動redis-server
啟動驗證:
ps -ef|grep redis
redis-cli -h locahost -p 6379 ping
由于 redis 是單線程的,推薦在一臺多核CPU機器上部署多個 redis 實例充分發(fā)揮。
持久化
redis 持久化支持2種:
RDB:快照方式,相當于 MySQL 中的 dump
AOF:寫日志方式,相當于 MySQL 中的 binlog,推薦使用
注意:
當同時開啟 RDB 和 AOF 的時候,redis啟動的時候會讀取 AOF 還原數(shù)據(jù)。
推薦:關閉 RDB 持久化機制,開啟 AOF
RDB
RDB是什么:
RDB方式的持久化是通過快照(snapshortting)完成的,當符合一定條件時Redis會自動將內存中所有數(shù)據(jù)完整的壓縮存儲到硬盤上。
RDB開啟條件由2個參數(shù) 時間 和 改動次數(shù)構成。如:save 900 1
RDB文件由2個參數(shù) dir 和 dbfilename 分別指定目錄 和 文件名
RDB方式是Redis默認的持久化方式。
觸發(fā)命令:
save 命令(阻塞)
bgsave 命令(fork過程阻塞)
主要觸發(fā)方式:
自動觸發(fā)規(guī)則(內部調用bgsave,不推薦開啟)
全量復制(內部調用bgsave)
過程:
執(zhí)行 save 或 bgsave 命令
生成新的 rdb 文件,如:temp-36985.rdb
覆蓋 rdb 文件,如:dump-6379.rdb
優(yōu)點:
啟動速度快
占用空間小
缺點:
容易丟失數(shù)據(jù)
時間復雜度O(n)
關閉RDB方式:
redis-cli config set save ""
注意:
RDB并不能真正的關閉,在主從復制時主從都會生成RDB文件
AOF
AOF是什么:
AOF是純文本文件,會記錄 Redis 的每次改動命令(不記錄查詢)。
AOF開啟條件:appendonly yes
AOF文件由2個參數(shù) dir 和 appendfilename 分別指定目錄 和 文件名
AOF方式 默認情況下Redis并沒有開啟。
由于每次改動都會記錄,產(chǎn)生2個問題:
每次改動都寫入硬盤,普通硬盤只能承受幾百次qps。通過寫入策略來調整
對同1個key執(zhí)行幾次操作就記錄幾次,冗余量特別大。通過 aof 文件重寫來調整
AOF文件有3種寫入策略:
always(每次寫入都會fsync同步到硬盤)
everysec(默認,1s寫1次)
no(并非不寫,交給系統(tǒng)控制預計30s寫1次)
AOF重寫:
重寫方式
手動執(zhí)行 bgrewriteaof 命令
自動觸發(fā)規(guī)則(通過指定最小aof文件和aof增長率來自動內部調用 bgrewriteaof)
過程
fork 出子進程
子進程執(zhí)行 bgrewriteaof 命令
父進程將新接收的命令,同時寫到 aof 文件和 aof_rewrite_buffer文件中。(在 aof 重寫時,可配置關閉aof寫入)
子進程將 aof_rewrite_buffer 文件追加到新 aof 文件中。
覆蓋舊的 aof 文件
注意:
采用 everysec 方式,最多可能丟失 2s 的數(shù)據(jù)。
主從復制
為什么需要主從復制:
通過持久化保證 Redis 在服務器重啟的情況下數(shù)據(jù)也不會丟失。但數(shù)據(jù)在一臺服務器上,如果服務器的硬盤壞了,也會導致數(shù)據(jù)丟失。為了避免單點故障,Redis 提供了主從復制高可用方案。
主從復制結構:
1個 master 可以有多個 slave
1個 slave 只能有1個 master
數(shù)據(jù)流向單向 master -> slave
開啟復制:
命令:--slaveof ip port
配置:slaveof ip port(默認配置都是master)
關閉復制:
slaveof no one
復制類型:
全量復制(首次 或者 網(wǎng)絡斷開時間比較長)
部分復制(在網(wǎng)絡抖動一定范圍的情況下,v2.8以上可配置復制緩存區(qū)repl-backlog-size)
全量復制過程:
slave 節(jié)點 發(fā)起 psync runid offset:psync ? -1
master 節(jié)點 返回 fullresync runid offset
master 節(jié)點 bgsave 保存當前數(shù)據(jù)到 rdb
master 節(jié)點 在此期間接收到新的數(shù)據(jù)存儲到 buffer 中
master 節(jié)點 send RDB、send buffer
slave 節(jié)點 flush old data
slave 節(jié)點 load RDB、load buffer
在master重啟(master 的run_id更新)和slave重啟(slave 的run_id丟失)時都會發(fā)生全量復制,通過 info server 可以查看run_id。
部分復制過程:
slave 節(jié)點 發(fā)起 psync runid offset
master 節(jié)點 確認 runid 和 offset沒問題后,發(fā)送增量數(shù)據(jù)
slave 節(jié)點 接收同步數(shù)據(jù)。
當全量復制完成 或 網(wǎng)絡抖動一定范圍 時,master 相當于 slave 的 client 進行增量更新數(shù)據(jù)。
Redis Sentinel
Redis-Sentinel是什么?
Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案
Redis-Sentinel本身也是一個獨立運行的進程,它能監(jiān)控多個 master-slave 集群,發(fā)現(xiàn) master宕機 后能進行自動故障轉移。
sentinel工作原理:
準備多個Redis Sentinel節(jié)點(建議至少3個節(jié)點,避免單點故障)
多個 Sentinel 節(jié)點發(fā)現(xiàn)并確認 master 主觀下線
超過 quorum 個 sentinel 判定確認 客觀下線
選出 1個 sentinel 節(jié)點作為領導
選出 1個 slave 節(jié)點作為 master
切換 slave 節(jié)點的 master 為新的 master
通知 client 主從變化
等待故障的 master 復活成為新的 slave
Client 不直接連接 Redis 節(jié)點,應該連接 Sentinel 節(jié)點獲取 Redis Info
2種下線判定:
sdown(subjectively down,主觀下線):每個 sentinel 判定 redis 節(jié)點下線。
odown(objectively down,客觀下線):超過 quorum 個 sentinel 判定 redis 節(jié)點下線。
啟動方式:
redis-sentinel /path/to/sentinel.conf
redis-server /path/to/sentinel.conf --sentinel
三個定時任務:
每個 Sentinel 節(jié)點每秒通過 redis 的?__sentinel__:hello?發(fā)布一條消息,宣布自己的存在。同時也訂閱來確定其他的 Sentinel 節(jié)點。
每個 Sentinel 節(jié)點每秒對其他 redis 節(jié)點執(zhí)行 ping。確定是否下線。
每個 Sentinel 節(jié)點每10秒 對 master 和 slave 執(zhí)行 info,確定 slaves。
配置模擬:
配置 Redis 開啟主從復制
配置 Sentinel 監(jiān)控主節(jié)點
常用的channel:
+switch-master:切換主節(jié)點
+convert-to-slave:切換從節(jié)點
+sdown:主觀下線
.NET Core環(huán)境開發(fā):
dotnet add package StackExchange.Redis
var options = new ConfigurationOptions() {CommandMap = CommandMap.Sentinel,EndPoints = { { "192.168.0.51", 26379}, {"192.168.0.51", 26381}, {"192.168.0.51", 26380} },AllowAdmin = true,TieBreaker = "",ServiceName = "mymaster",SyncTimeout = 5000};var sentinelConn = ConnectionMultiplexer.Connect(options);var master = sentinelConn.GetServer("192.168.0.51",26381).SentinelGetMasterAddressByName("mymaster");// ...var conn = ConnectionMultiplexer.Connect(master); sentinelConn.GetSubscriber().Subscribe("+switch-master", (channel, message) => { ? ?// mymaster 192.168.0.51 6380 192.168.0.51 6381Console.WriteLine((string)message); ? ?// ...conn = ConnectionMultiplexer.Connect(ip);conn.GetDatabase().StringSet("hello","故障切換后值"); }); sentinelConn.GetSubscriber().Subscribe("+convert-to-slave", (channel, message) => { ? ?// slave 192.168.0.51:6379 192.168.0.51 6379 @ mymaster 192.168.0.51 6380Console.WriteLine((string)message); });conn.GetDatabase().StringSet("hello","原始值");注意:
所有Sentinel和Redis不能在同一個節(jié)點
Redis Cluster
實際上大部分場景下,Redis Sentinel已經(jīng)足夠好。請根據(jù)實際情況采用 Redis Cluster。
Redis Cluster 采用虛擬槽分區(qū)方式(16384個虛擬槽)。
原因:
需要更高的qps(超過 10w/s)
需要更高的數(shù)據(jù)量(超過 500G)
需要更高的帶寬(超過 1000M)
常用命令:
redis-cli -h localhost -p 6382 cluster info:查看集群基本信息
redis-cli -h localhost -p 6382 cluster slots:查看集群slot信息
redis-cli -h localhost -p 6382 cluster nodes:查看集群node信息
redis-cli -c:move自動跳轉執(zhí)行
yum install -y redis-trib:官方提供了基于 ruby 的工具方便部署
搭建 Cluster 過程:
配置
cluster-enabled:yes
cluster-node-timeout 15000
cluster-require-full-coverage no
cluster-config-file node-${port}.conf
meet
redis-cli cluster meet ip port
分配槽(0-16383)
redis-cli cluster addslots {0....5461}
分配主從(node-id)
redis-cli cluster replicate {nodeid}
redis-cli 搭建:
echo "停止當前所有redis-server + redis-sentinel"; mkdir /etc/redis ps -x | grep redis | grep -v grep | awk '{print $1}' | xargs -r killsleep 1echo "啟動6個 redis + meet";for port in 7000 7001 7002 7003 7004 7005;doecho -e "daemonize yes\nport $port\npidfile /var/run/redis-$port.pid\nlogfile /var/log/redis/redis-$port.log\ncluster-enabled yes\ncluster-config-file nodes-$port.conf\ncluster-require-full-coverage no" > /etc/redis/redis-$port.conf redis-server /etc/redis/redis-$port.confdonefor port in 7000 7001 7002 7003 7004 7005;doredis-cli -p $port FLUSHALLredis-cli -p $port cluster reset soft ? ?if [ $port != 7000 ];thenredis-cli -p 7000 cluster meet 127.0.0.1 $portfidonesleep 1echo "分配 16383 槽"; redis-cli -p 7000 cluster addslots {0..5461} redis-cli -p 7001 cluster addslots {5462..10922} redis-cli -p 7002 cluster addslots {10922..16383}echo "配置 replication"redis-cli -p 7003 cluster replicate `redis-cli -p 7000 cluster nodes | grep myself | awk '{print $1}'` redis-cli -p 7004 cluster replicate `redis-cli -p 7001 cluster nodes | grep myself | awk '{print $1}'` redis-cli -p 7005 cluster replicate `redis-cli -p 7002 cluster nodes | grep myself | awk '{print $1}'`redis-trib搭建:
準備節(jié)點
使用redis-trib搭建
redis-trib 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 create 會自動meet、addslots、replicate。
配置
查看去除注釋的配置:cat /etc/redis.conf | grep -v '^#' | grep -v '^$'
設置配置:config set key value
查詢所有配置:config get *
基礎配置
| daemonize | no | yes(docker環(huán)境例外) | 是否以守護進程方式啟動 |
| port | 6379 | - | redis服務監(jiān)聽端口 |
| pidfile | /var/run/redis.pid | /var/run/redis-{port}.pid | pid文件 |
| logfile | /var/log/redis/redis.log | /var/log/redis/redis-{port}.log | 日志文件名:redis工作時產(chǎn)生的日志。 |
| dir | /var/lib/redis | - | rdb文件和aof文件目錄。推薦使用大文件目錄。(不指定則為當前目錄) |
| protected-mode | yes | - | 限制為127.0.0.1訪問。啟用條件:沒有bindIP 和 沒有設置密碼 |
RDB配置
| dbfilename | dump.rdb | dump-{port}.rdb | rdb文件名 |
| rdbcompression | yes | yes | 壓縮格式 |
| stop-writes-on-bgsave-error | yes | yes | 出現(xiàn)錯誤時,停止新的寫入 |
| rdbchecksum | yes | yes | 數(shù)據(jù)完整性校驗 |
AOF配置
| appendonly | no | yes | 是否開啟 aof 模式 |
| appendfilename | "appendonly.aof" | "appendonly-{port}.aof" | aof文件名 |
| appendfsync | everysec | everysec | fsync方式 |
| no-appendfsync-on-rewrite | no(安全) | yes(高性能) | 在 aof 重寫時,是否停止fsync |
| auto-aof-rewrite-min-size | 64mb | - | aof文件重寫的最小大小 |
| auto-aof-rewrite-percentage | 100 | - | aof文件增長率 |
| aof-load-truncated | yes | yes | 當 aof 文件不完整的時候,將完整的部分加載 |
主從復制配置
| slowlog-max-len | 128 | 1000 | 慢查詢隊列長度 |
| slowlog-log-slow-than | 10000 | 1000(qps1w) | 慢查詢閾值(單位:微秒) |
| slaveof | ip port | - | 主從復制配置 |
| slave-read-only | yes | yes | 從節(jié)點只讀 |
| repl-backlog-size | 1048576 | 10M | 復制緩存區(qū),可以再原有基礎上稍微增加 |
Sentinel配置
| daemonize | no | yes | 是否以守護進程方式啟動 |
| port | 26379 | {port} | sentinel監(jiān)聽端口 |
| dir | /tmp | - | 工作目錄 |
| sentinel monitor | mymaster 127.0.0.1 6379 2 | - | odown(objectively down,客觀下線)規(guī)則:masterName ip port quorum |
| sentinel down-after-milliseconds | mymaster 30000 | - | sdown(subjectively down,主觀下線)規(guī)則:masterName timeout(單位:毫秒) |
| sentinel parallel-syncs | mymaster 1 | - | 并發(fā)同步數(shù)量 |
| sentinel failover-timeout | mymaster 180000 | - | 多長時間內不再故障轉移(單位:毫秒) |
| logfile | /var/log/redis/sentinel.log | /var/log/redis/sentinel-{port}.log | 日志文件 |
Cluster配置
| cluster-enabled | no | yes | 開啟cluster模式 |
| cluster-node-timeout | 15000 | - | 故障轉移時間,主觀下線超時時間 |
| cluster-config-file | nodes-{port}.conf | cluster配置 | |
| cluster-require-full-coverage | yes | no | cluster 所有節(jié)點全部在線才提供服務 |
常見問題
redis是單線程嗎?為什么這么快?
redis其實不是單線程(fsync,bgsave),一次只能執(zhí)行一條命令。
慢查詢
查詢慢查詢隊列:slowlog get
客戶端請求的生命周期:
發(fā)送命令
排隊
執(zhí)行命令
返回結果
慢查詢發(fā)送在第三個階段(執(zhí)行命令),客戶端超時不一定是慢查詢。
fork
fork本身是同步操作
內存越大耗時越長
info:latest_fork_usec
規(guī)避全量復制
首次全量復制:不可避免
runid 不匹配:故障轉移
復制緩沖區(qū)不足:配置repl_backlog_size調整大
常用命令
KEYS pattern :查詢keys
DBSIZE :查詢所有鍵的數(shù)量
EXISTS key :查詢指定key是否存在
TYPE key :查詢key的類型
DEL key :刪除指定key
INFO :查看server 信息如:INFO memory
INFO 信息:
used_memory redis 當前使用的內存總量
used_memory_rss redis 當前使用的內存總量(包含內存碎片)
used_memory_peak redis 使用的內存總量峰值
總結
以上是生活随笔為你收集整理的Redis 实战之主从复制、高可用、分布式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .NET微服务调查结果
- 下一篇: Redis基本使用及百亿数据量中的使用技