目錄
- 簡介
- 持久化
- 主從複製
- 高可用 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
port $port
pidfile /var/run/redis-$port.pid
logfile /var/log/redis/redis-$port.log
" > /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
port $port
dir /tmp
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 3000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 60000
logfile /var/log/redis/sentinel-$port.log
" > /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
port $port
pidfile /var/run/redis-$port.pid
logfile /var/log/redis/redis-$port.log
cluster-enabled yes
cluster-config-file nodes-$port.conf
cluster-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 使用的記憶體總量峰值