【中間件】Redis 實戰之主從複製、高可用、分散式

来源:https://www.cnblogs.com/neverc/archive/2018/08/29/9554498.html
-Advertisement-
Play Games

目錄 簡介 持久化 主從複製 高可用 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種:

  1. RDB:快照方式,相當於 MySQL 中的 dump
  2. AOF:寫日誌方式,相當於 MySQL 中的 binlog,推薦使用

註意:

  1. 當同時開啟 RDB 和 AOF 的時候,redis啟動的時候會讀取 AOF 還原數據。
  2. 推薦:關閉 RDB 持久化機制,開啟 AOF

RDB

RDB是什麼:

  1. RDB方式的持久化是通過快照(snapshortting)完成的,當符合一定條件時Redis會自動將記憶體中所有數據完整的壓縮存儲到硬碟上。
  2. RDB開啟條件由2個參數 時間 和 改動次數構成。如:save 900 1
  3. RDB文件由2個參數 dir 和 dbfilename 分別指定目錄 和 文件名
  4. RDB方式是Redis預設的持久化方式。

觸發命令:

  1. save 命令(阻塞)
  2. bgsave 命令(fork過程阻塞)

主要觸發方式:

  1. 自動觸發規則(內部調用bgsave,不推薦開啟)
  2. 全量複製(內部調用bgsave)

過程:

  1. 執行 save 或 bgsave 命令
  2. 生成新的 rdb 文件,如:temp-36985.rdb
  3. 覆蓋 rdb 文件,如:dump-6379.rdb

優點:

  1. 啟動速度快
  2. 占用空間小

缺點:

  1. 容易丟失數據
  2. 時間複雜度O(n)

關閉RDB方式:

redis-cli config set save ""

註意:

RDB並不能真正的關閉,在主從複製時主從都會生成RDB文件

AOF

AOF是什麼:

  1. AOF是純文本文件,會記錄 Redis 的每次改動命令(不記錄查詢)。
  2. AOF開啟條件:appendonly yes
  3. AOF文件由2個參數 dir 和 appendfilename 分別指定目錄 和 文件名
  4. AOF方式 預設情況下Redis並沒有開啟。

由於每次改動都會記錄,產生2個問題:

  1. 每次改動都寫入硬碟,普通硬碟只能承受幾百次qps。通過寫入策略來調整
  2. 對同1個key執行幾次操作就記錄幾次,冗餘量特別大。通過 aof 文件重寫來調整

AOF文件有3種寫入策略:

  1. always(每次寫入都會fsync同步到硬碟)
  2. everysec(預設,1s寫1次)
  3. no(並非不寫,交給系統控制預計30s寫1次)

AOF重寫:

  1. 重寫方式
    1. 手動執行 bgrewriteaof 命令
    2. 自動觸發規則(通過指定最小aof文件和aof增長率來自動內部調用 bgrewriteaof)
  2. 過程
    1. fork 出子進程
    2. 子進程執行 bgrewriteaof 命令
    3. 父進程將新接收的命令,同時寫到 aof 文件和 aof_rewrite_buffer文件中。(在 aof 重寫時,可配置關閉aof寫入)
    4. 子進程將 aof_rewrite_buffer 文件追加到新 aof 文件中。
    5. 覆蓋舊的 aof 文件

註意:

  1. 採用 everysec 方式,最多可能丟失 2s 的數據。

主從複製

為什麼需要主從複製:

通過持久化保證 Redis 在伺服器重啟的情況下數據也不會丟失。但數據在一臺伺服器上,如果伺服器的硬碟壞了,也會導致數據丟失。為了避免單點故障,Redis 提供了主從複製高可用方案。

主從複製結構:

  1. 1個 master 可以有多個 slave
  2. 1個 slave 只能有1個 master
  3. 數據流向單向 master -> slave

開啟複製:

  1. 命令:--slaveof ip port
  2. 配置:slaveof ip port(預設配置都是master)

關閉複製:

slaveof no one

複製類型:

  1. 全量複製(首次 或者 網路斷開時間比較長)
  2. 部分複製(在網路抖動一定範圍的情況下,v2.8以上可配置複製緩存區repl-backlog-size)

全量複製過程:

  1. slave 節點 發起 psync runid offset:psync ? -1
  2. master 節點 返回 fullresync runid offset
  3. master 節點 bgsave 保存當前數據到 rdb
  4. master 節點 在此期間接收到新的數據存儲到 buffer 中
  5. master 節點 send RDB、send buffer
  6. slave 節點 flush old data
  7. slave 節點 load RDB、load buffer

在master重啟(master 的run_id更新)和slave重啟(slave 的run_id丟失)時都會發生全量複製,通過 info server 可以查看run_id。

部分複製過程:

  1. slave 節點 發起 psync runid offset
  2. master 節點 確認 runid 和 offset沒問題後,發送增量數據
  3. slave 節點 接收同步數據。

當全量複製完成 或 網路抖動一定範圍 時,master 相當於 slave 的 client 進行增量更新數據。

Redis Sentinel

Redis-Sentinel是什麼?

  1. Redis-Sentinel是Redis官方推薦的高可用性(HA)解決方案
  2. Redis-Sentinel本身也是一個獨立運行的進程,它能監控多個 master-slave 集群,發現 master宕機 後能進行自動故障轉移。

sentinel工作原理:

  1. 準備多個Redis Sentinel節點(建議至少3個節點,避免單點故障)
  2. 多個 Sentinel 節點發現並確認 master 主觀下線
  3. 超過 quorum 個 sentinel 判定確認 客觀下線
  4. 選出 1個 sentinel 節點作為領導
  5. 選出 1個 slave 節點作為 master
  6. 切換 slave 節點的 master 為新的 master
  7. 通知 client 主從變化
  8. 等待故障的 master 複活成為新的 slave
  9. Client 不直接連接 Redis 節點,應該連接 Sentinel 節點獲取 Redis Info

2種下線判定:

  1. sdown(subjectively down,主觀下線):每個 sentinel 判定 redis 節點下線。
  2. odown(objectively down,客觀下線):超過 quorum 個 sentinel 判定 redis 節點下線。

啟動方式:

  1. redis-sentinel /path/to/sentinel.conf
  2. redis-server /path/to/sentinel.conf --sentinel

三個定時任務:

  1. 每個 Sentinel 節點每秒通過 redis 的 __sentinel__:hello 發佈一條消息,宣佈自己的存在。同時也訂閱來確定其他的 Sentinel 節點。
  2. 每個 Sentinel 節點每秒對其他 redis 節點執行 ping。確定是否下線。
  3. 每個 Sentinel 節點每10秒 對 master 和 slave 執行 info,確定 slaves。

配置模擬:

  1. 配置 Redis 開啟主從複製
  2. 配置 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","原始值");

註意:

  1. 所有Sentinel和Redis不能在同一個節點

Redis Cluster

實際上大部分場景下,Redis Sentinel已經足夠好。請根據實際情況採用 Redis Cluster。

Redis Cluster 採用虛擬槽分區方式(16384個虛擬槽)。

原因:

  • 需要更高的qps(超過 10w/s)
  • 需要更高的數據量(超過 500G)
  • 需要更高的帶寬(超過 1000M)

常用命令:

  1. redis-cli -h localhost -p 6382 cluster info:查看集群基本信息
  2. redis-cli -h localhost -p 6382 cluster slots:查看集群slot信息
  3. redis-cli -h localhost -p 6382 cluster nodes:查看集群node信息
  4. redis-cli -c:move自動跳轉執行
  5. yum install -y redis-trib:官方提供了基於 ruby 的工具方便部署

搭建 Cluster 過程:

  1. 配置
    cluster-enabled:yes
    cluster-node-timeout 15000
    cluster-require-full-coverage no
    cluster-config-file node-${port}.conf
  2. meet
    redis-cli cluster meet ip port
  3. 分配槽(0-16383)
    redis-cli cluster addslots {0....5461}
  4. 分配主從(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搭建:

  1. 準備節點
  2. 使用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

客戶端請求的生命周期:

  1. 發送命令
  2. 排隊
  3. 執行命令
  4. 返回結果

慢查詢發送在第三個階段(執行命令),客戶端超時不一定是慢查詢。

fork

  1. fork本身是同步操作
  2. 記憶體越大耗時越長
  3. 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 使用的記憶體總量峰值

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>事件冒泡-提示框</title> 7 </head> 8 <style> 9 button { 10 width: 160px; 11 ...
  • 展示: ...
  • 前言 對於vue cli的強大,使用過的人都知道,極大的幫助我們降低了vue的入門門檻 最近在看webpack4,深感知識淺薄,這兩天也一直在思考cli的配置,藉助一些別人的實踐,嘗試自己搭建vue的項目,這裡使用webpack4版本,之前我在網上查找別人的vue項目搭建,但是都是webpack3的 ...
  • 強弱類型的語言,簡單來區分就是會不會隱式轉換數據類型, 比如最常見的數值型與字元串之間的轉換 強類型的語言 : java , .net , python等 弱類型的語言: php JavaScript等 舉個python 與 JavaScript的例子 python a = 5 b = '5' pr ...
  • angularjs通過ng-change和watch兩種方式實現對錶單輸入改變的監控 ...
  • 面試中2次被問到過這個知識點,實際開發中,應用事件委托也比較常見。JS中事件委托的實現主要依賴於 事件冒泡 。那什麼是事件冒泡?就是事件從最深的節點開始,然後逐步向上傳播事件,舉個例子:頁面上有這麼一個節點樹,div>ul>li>a;比如給最裡面的a加一個click點擊事件,那麼這個事件就會一層一層 ...
  • 近年來,一些動態特性開始作為規範的一部分,出現在CSS語言中。在本文,你將學會如何使用CSS變數,並把它集成到你的CSS開發流程中,讓你的樣式表更好維護,且減少重覆。 ...
  • 我們知道關鍵字function用來定義函數;函數定義可以寫成函數定義表達式,也可以寫成語句的形式。例如下麵的兩種寫法 儘管函數聲明語句和函數定義表達式包含相同的函數名;但它們之間還是有區別的。 相同點:兩種方式都創建了新的函數對象;兩者都會被“提前”(函數語句中定義的函數被顯示的提前到腳本或則函數的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...