目錄 簡介 持久化 主從複製 高可用 Redis Sentinel .NET Core開發 分散式 Redis Cluster 配置說明 常見問題 簡介 本節內容基於 CentOS 7.4.1708,Redis 3.2.12 環境實驗。 Redis 是一個開源的高性能鍵值對資料庫。 安裝: 特性: ...
目錄
- 簡介
- 持久化
- 主從複製
- 高可用 Redis-Sentinel
- .NET Core開發
- 分散式 Redis-Cluster
- 配置說明
- 常見問題
簡介
本節內容基於 CentOS 7.4.1708,Redis 3.2.12 環境實驗。
Redis 是一個開源的高性能鍵值對資料庫。
安裝:yum install -y redis
特性:
- 高性能 Key-Value 伺服器
- 多種數據結構
- 豐富功能
- 緩存(get|set)
- 計數器(incre)
- 消息隊列(publish|subcrib)
- 高可用(v2.8 redis-sentinel)
- 分散式(v3.0 redis-cluster)
可執行文件:
- redis-server:服務端
- redis-cli:客戶端
- redis-benchmark:性能測試工具
- redis-check-aof:aof修複工具
- redis-check-dump:rdb修複工具
- redis-sentinel:sentinel服務端
啟動方式:
- 最簡啟動:預設配置直接啟動redis-server
- 動態參數啟動:命令行指定配置啟動redis-server
- 配置文件啟動(推薦):指定配置文件啟動redis-server
啟動驗證:
ps -ef|grep redis
redis-cli -h locahost -p 6379 ping
由於 redis 是單線程的,推薦在一臺多核CPU機器上部署多個 redis 實例充分發揮。
持久化
redis 持久化支持2種:
- RDB:快照方式,相當於 MySQL 中的 dump
- AOF:寫日誌方式,相當於 MySQL 中的 binlog,推薦使用
註意:
- 當同時開啟 RDB 和 AOF 的時候,redis啟動的時候會讀取 AOF 還原數據。
- 推薦:關閉 RDB 持久化機制,開啟 AOF
RDB
RDB是什麼:
- RDB方式的持久化是通過快照(snapshortting)完成的,當符合一定條件時Redis會自動將記憶體中所有數據完整的壓縮存儲到硬碟上。
- RDB開啟條件由2個參數 時間 和 改動次數構成。如:save 900 1
- RDB文件由2個參數 dir 和 dbfilename 分別指定目錄 和 文件名
- RDB方式是Redis預設的持久化方式。
觸發命令:
- save 命令(阻塞)
- bgsave 命令(fork過程阻塞)
主要觸發方式:
- 自動觸發規則(內部調用bgsave,不推薦開啟)
- 全量複製(內部調用bgsave)
過程:
- 執行 save 或 bgsave 命令
- 生成新的 rdb 文件,如:temp-36985.rdb
- 覆蓋 rdb 文件,如:dump-6379.rdb
優點:
- 啟動速度快
- 占用空間小
缺點:
- 容易丟失數據
- 時間複雜度O(n)
關閉RDB方式:
redis-cli config set save ""
註意:
RDB並不能真正的關閉,在主從複製時主從都會生成RDB文件
AOF
AOF是什麼:
- AOF是純文本文件,會記錄 Redis 的每次改動命令(不記錄查詢)。
- AOF開啟條件:appendonly yes
- AOF文件由2個參數 dir 和 appendfilename 分別指定目錄 和 文件名
- AOF方式 預設情況下Redis並沒有開啟。
由於每次改動都會記錄,產生2個問題:
- 每次改動都寫入硬碟,普通硬碟只能承受幾百次qps。通過寫入策略來調整
- 對同1個key執行幾次操作就記錄幾次,冗餘量特別大。通過 aof 文件重寫來調整
AOF文件有3種寫入策略:
- always(每次寫入都會fsync同步到硬碟)
- everysec(預設,1s寫1次)
- no(並非不寫,交給系統控制預計30s寫1次)
AOF重寫:
- 重寫方式
- 手動執行 bgrewriteaof 命令
- 自動觸發規則(通過指定最小aof文件和aof增長率來自動內部調用 bgrewriteaof)
- 過程
- fork 出子進程
- 子進程執行 bgrewriteaof 命令
- 父進程將新接收的命令,同時寫到 aof 文件和 aof_rewrite_buffer文件中。(在 aof 重寫時,可配置關閉aof寫入)
- 子進程將 aof_rewrite_buffer 文件追加到新 aof 文件中。
- 覆蓋舊的 aof 文件
註意:
- 採用 everysec 方式,最多可能丟失 2s 的數據。
主從複製
為什麼需要主從複製:
通過持久化保證 Redis 在伺服器重啟的情況下數據也不會丟失。但數據在一臺伺服器上,如果伺服器的硬碟壞了,也會導致數據丟失。為了避免單點故障,Redis 提供了主從複製高可用方案。
主從複製結構:
- 1個 master 可以有多個 slave
- 1個 slave 只能有1個 master
- 數據流向單向 master -> slave
開啟複製:
- 命令:
--slaveof ip port
- 配置:
slaveof ip port
(預設配置都是master)
關閉複製:
slaveof no one
複製類型:
- 全量複製(首次 或者 網路斷開時間比較長)
- 部分複製(在網路抖動一定範圍的情況下,v2.8以上可配置複製緩存區repl-backlog-size)
全量複製過程:
- slave 節點 發起 psync runid offset:
psync ? -1
- master 節點 返回 fullresync runid offset
- master 節點 bgsave 保存當前數據到 rdb
- master 節點 在此期間接收到新的數據存儲到 buffer 中
- master 節點 send RDB、send buffer
- slave 節點 flush old data
- slave 節點 load RDB、load buffer
在master重啟(master 的run_id更新)和slave重啟(slave 的run_id丟失)時都會發生全量複製,通過 info server 可以查看run_id。
部分複製過程:
- slave 節點 發起 psync runid offset
- master 節點 確認 runid 和 offset沒問題後,發送增量數據
- slave 節點 接收同步數據。
當全量複製完成 或 網路抖動一定範圍 時,master 相當於 slave 的 client 進行增量更新數據。
Redis Sentinel
Redis-Sentinel是什麼?
- Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案
- Redis-Sentinel本身也是一個獨立運行的進程,它能監控多個 master-slave 集群,發現 master宕機 後能進行自動故障轉移。
sentinel工作原理:
- 準備多個Redis Sentinel節點(建議至少3個節點,避免單點故障)
- 多個 Sentinel 節點發現並確認 master 主觀下線
- 超過 quorum 個 sentinel 判定確認 客觀下線
- 選出 1個 sentinel 節點作為領導
- 選出 1個 slave 節點作為 master
- 切換 slave 節點的 master 為新的 master
- 通知 client 主從變化
- 等待故障的 master 複活成為新的 slave
- Client 不直接連接 Redis 節點,應該連接 Sentinel 節點獲取 Redis Info
2種下線判定:
- sdown(subjectively down,主觀下線):每個 sentinel 判定 redis 節點下線。
- odown(objectively down,客觀下線):超過 quorum 個 sentinel 判定 redis 節點下線。
啟動方式:
redis-sentinel /path/to/sentinel.conf
redis-server /path/to/sentinel.conf --sentinel
三個定時任務:
- 每個 Sentinel 節點每秒通過 redis 的
__sentinel__:hello
發佈一條消息,宣佈自己的存在。同時也訂閱來確定其他的 Sentinel 節點。 - 每個 Sentinel 節點每秒對其他 redis 節點執行 ping。確定是否下線。
- 每個 Sentinel 節點每10秒 對 master 和 slave 執行 info,確定 slaves。
配置模擬:
- 配置 Redis 開啟主從複製
- 配置 Sentinel 監控主節點
echo "停止當前所有redis-server + redis-sentinel";
ps -x | grep redis | grep -v grep | awk '{print $1}' | xargs -r kill
echo "生成並啟動3個 redis 配置";
for port in 6379 6380 6381 ;do
echo -e "daemonize yes\nport $port\npidfile /var/run/redis-$port.pid\nlogfile /var/log/redis/redis-$port.log\n" > /etc/redis/redis-$port.conf
if [ $port != 6379 ];then
echo "slaveof 127.0.0.1 6379" >> /etc/redis/redis-$port.conf
fi
redis-server /etc/redis/redis-$port.conf
done
echo "生成並啟動3個 redis-sentinel 配置";
for port in 26379 26380 26381 ;do
echo -e "daemonize yes\nport $port\ndir /tmp\nsentinel monitor mymaster 127.0.0.1 6379 2\nsentinel down-after-milliseconds mymaster 3000\nsentinel parallel-syncs mymaster 1\nsentinel failover-timeout mymaster 60000\nlogfile /var/log/redis/sentinel-$port.log\n" > /etc/redis/redis-sentinel-$port.conf
redis-sentinel /etc/redis/redis-sentinel-$port.conf
done
echo "結束";
常用的channel:
- +switch-master:切換主節點
- +convert-to-slave:切換從節點
- +sdown:主觀下線
.NET Core環境開發:
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 6381
Console.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 6380
Console.WriteLine((string)message);
});
conn.GetDatabase().StringSet("hello","原始值");
註意:
- 所有Sentinel和Redis不能在同一個節點
Redis Cluster
實際上大部分場景下,Redis Sentinel已經足夠好。請根據實際情況採用 Redis Cluster。
Redis Cluster 採用虛擬槽分區方式(16384個虛擬槽)。
原因:
- 需要更高的qps(超過 10w/s)
- 需要更高的數據量(超過 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自動跳轉執行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 kill
sleep 1
echo "啟動6個 redis + meet";
for port in 7000 7001 7002 7003 7004 7005;do
echo -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.conf
done
for port in 7000 7001 7002 7003 7004 7005;do
redis-cli -p $port FLUSHALL
redis-cli -p $port cluster reset soft
if [ $port != 7000 ];then
redis-cli -p 7000 cluster meet 127.0.0.1 $port
fi
done
sleep 1
echo "分配 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搭建:
- 準備節點
- 使用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環境例外) | 是否以守護進程方式啟動 |
port | 6379 | - | redis服務監聽埠 |
pidfile | /var/run/redis.pid | /var/run/redis-{port}.pid | pid文件 |
logfile | /var/log/redis/redis.log | /var/log/redis/redis-{port}.log | 日誌文件名:redis工作時產生的日誌。 |
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 | 出現錯誤時,停止新的寫入 |
rdbchecksum | yes | yes | 數據完整性校驗 |
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 | 從節點只讀 |
repl-backlog-size | 1048576 | 10M | 複製緩存區,可以再原有基礎上稍微增加 |
Sentinel配置
配置項 | 預設值 | 推薦值 | 說明 |
---|---|---|---|
daemonize | no | yes | 是否以守護進程方式啟動 |
port | 26379 | {port} | sentinel監聽埠 |
dir | /tmp | - | 工作目錄 |
sentinel monitor | mymaster 127.0.0.1 6379 2 | - | odown(objectively down,客觀下線)規則:masterName ip port quorum |
sentinel down-after-milliseconds | mymaster 30000 | - | sdown(subjectively down,主觀下線)規則:masterName timeout(單位:毫秒) |
sentinel parallel-syncs | mymaster 1 | - | 併發同步數量 |
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 所有節點全部線上才提供服務 |
常見問題
redis是單線程嗎?為什麼這麼快?
redis其實不是單線程(fsync,bgsave),一次只能執行一條命令。
慢查詢
查詢慢查詢隊列:slowlog get
客戶端請求的生命周期:
- 發送命令
- 排隊
- 執行命令
- 返回結果
慢查詢發送在第三個階段(執行命令),客戶端超時不一定是慢查詢。
fork
- fork本身是同步操作
- 記憶體越大耗時越長
- info:latest_fork_usec
規避全量複製
- 首次全量複製:不可避免
- runid 不匹配:故障轉移
- 複製緩衝區不足:配置
repl_backlog_size
調整大
常用命令
- KEYS pattern :查詢keys
- DBSIZE :查詢所有鍵的數量
- 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 使用的記憶體總量峰值