容及相關(guān)實(shí)驗(yàn))
Redis 內(nèi)容及相關(guān)實(shí)驗(yàn)Redis 就像一個(gè)請(qǐng)了個(gè)「24 小時(shí)不睡覺(jué)、記憶力還超群」的數(shù)據(jù)管家你把數(shù)據(jù)丟給它它直接放在內(nèi)存里隨取隨用快得離譜。目錄Redis 簡(jiǎn)介Redis 安裝部署Redis 主從復(fù)制Redis 哨兵模式 SentinelRedis Cluster 集群三種高可用方案對(duì)比小結(jié)總結(jié)一、Redis 簡(jiǎn)介1.1 Redis 是什么RedisRemote Dictionary Server是一個(gè)開源的、基于內(nèi)存的鍵值對(duì)Key-Value數(shù)據(jù)庫(kù)。它把所有數(shù)據(jù)放在內(nèi)存里因此讀寫速度能達(dá)到10 萬(wàn) QPS同時(shí)通過(guò) RDB / AOF 把數(shù)據(jù)落地到磁盤宕機(jī)后也能恢復(fù)所以它既快又不怕丟。它和 MySQL 這種把數(shù)據(jù)放硬盤、用的時(shí)候再讀的傳統(tǒng)關(guān)系型數(shù)據(jù)庫(kù)不一樣MySQL 是倉(cāng)庫(kù)規(guī)整但取貨慢Redis 是手邊的保險(xiǎn)柜隨手就能拿。典型使用場(chǎng)景場(chǎng)景說(shuō)明緩存Cache把熱點(diǎn)數(shù)據(jù)放 Redis擋在數(shù)據(jù)庫(kù)前面扛住高并發(fā)讀數(shù)據(jù)庫(kù)DB直接當(dāng)主存儲(chǔ)配合持久化保證數(shù)據(jù)不丟消息隊(duì)列 / 發(fā)布訂閱用 List、Stream 做輕量隊(duì)列、用 Pub/Sub 做消息廣播排行榜 / 計(jì)數(shù)器用 ZSet、incr 做實(shí)時(shí)排名、訪問(wèn)計(jì)數(shù)分布式鎖用SET key value NX實(shí)現(xiàn)跨進(jìn)程互斥1.2 為什么這么快數(shù)據(jù)在內(nèi)存沒(méi)有磁盤 IO 瓶頸單線程模型避免了多線程上下文切換和鎖競(jìng)爭(zhēng)6.0 后引入多線程 IO但命令執(zhí)行仍是單線程IO 多路復(fù)用epoll一個(gè)線程就能同時(shí)hold住海量連接高效的數(shù)據(jù)結(jié)構(gòu)SDS、跳表、哈希表等底層結(jié)構(gòu)都為性能量身設(shè)計(jì)。1.3 數(shù)據(jù)類型Redis 的 key 都是字符串value 支持多種結(jié)構(gòu)。記住五大基礎(chǔ)類型 擴(kuò)展類型就夠了String字符串最常用可存文本、數(shù)字、序列化對(duì)象支持incr自增List列表雙向鏈表可做隊(duì)列 / 棧Hash哈希鍵值對(duì)集合適合存對(duì)象屬性如用戶資料Set集合無(wú)序去重支持交集/并集做共同好友、抽獎(jiǎng)ZSet有序集合帶分?jǐn)?shù)的有序集合做排行榜神器擴(kuò)展類型Bitmaps位圖在線狀態(tài)、HyperLogLogUV 統(tǒng)計(jì)、Geospatial地理附近的人、Streams消息流。1.4 持久化RDB 與 AOF內(nèi)存數(shù)據(jù)斷電即失所以 Redis 提供兩種持久化方式生產(chǎn)環(huán)境建議兩個(gè)都開RDB快照定時(shí)把內(nèi)存全量數(shù)據(jù) dump 成.rdb二進(jìn)制文件。文件小、恢復(fù)快適合備份缺點(diǎn)是可能丟最后一次快照之后的數(shù)據(jù)。AOF日志把每一條寫命令追加到.aof文件。數(shù)據(jù)更安全可配置每秒/每次刷盤文件大、恢復(fù)稍慢。重啟時(shí) Redis優(yōu)先用 AOF恢復(fù)數(shù)據(jù)更全RDB 主要用來(lái)做快速備份和主從全量同步。二、Redis 安裝部署實(shí)驗(yàn)實(shí)驗(yàn)環(huán)境3 臺(tái)機(jī)器redis-node1/2/3IP 分別為172.25.254.10/20/30系統(tǒng)已配置好主機(jī)名解析。以下以 node1 為例其余節(jié)點(diǎn)同樣操作。2.1 安裝依賴[rootredis-node1 ~]# dnf install make gcc initscripts -y2.2 源碼編譯安裝[rootredis-node1 ~]# wget https://download.redis.io/releases/redis-7.4.8.tar.gz[rootredis-node1 ~]# tar zxf redis-7.4.8.tar.gz[rootredis-node1 ~]# cd redis-7.4.8/[rootredis-node1 redis-7.4.8]# make make install2.3 用腳本生成服務(wù)install_server.shRedis 自帶utils/install_server.sh可以一鍵生成 systemd 可管理的服務(wù)。systemd環(huán)境下腳本默認(rèn)會(huì)退出需要先把它的 systemd 檢測(cè)注釋掉[rootredis-node1 redis-7.4.8]# cd utils/[rootredis-node1 utils]# vim install_server.sh# 把下面這段 systemd 檢測(cè)注釋掉否則腳本直接 exit#_pid_1_exe$(readlink -f /proc/1/exe)#if [ ${_pid_1_exe##*/} systemd ]#then# echo This systems seems to use systemd.# ...#fi[rootredis-node1 utils]# ./install_server.sh交互提示一路回車用默認(rèn)值關(guān)鍵配置如下Please select the redis port for this instance: [6379] Please select the redis config file name [/etc/redis/6379.conf] /etc/redis/redis.conf Please select the redis log file name [/var/log/redis_6379.log] Please select the data directory for this instance [/var/lib/redis/6379] Please select the redis executable path [/usr/local/bin/redis-server] ... Installation successful!2.4 放開監(jiān)聽并啟動(dòng)編輯配置文件關(guān)閉保護(hù)模式、允許所有網(wǎng)卡監(jiān)聽[rootredis-node1 utils]# vim /etc/redis/redis.conf# 約 89 行允許所有 IP 監(jiān)聽bind* -::*# 約 113 行關(guān)閉保護(hù)模式實(shí)驗(yàn)環(huán)境生產(chǎn)請(qǐng)配合密碼/防火墻protected-mode no[rootredis-node1 utils]# systemctl daemon-reload[rootredis-node1 utils]# systemctl start redis_6379.service[rootredis-node1 utils]# systemctl status redis_6379.service2.5 驗(yàn)證端口監(jiān)聽[rootredis-node1 ~]# netstat -antlpe | grep redistcp00127.0.0.1:63790.0.0.0:* LISTEN... redis-server tcp00::1:6379 :::* LISTEN... redis-server看到6379端口處于LISTEN說(shuō)明 Redis 已經(jīng)跑起來(lái)了。node2、node3用同樣的步驟安裝即可。三、Redis 主從復(fù)制3.1為什么需要主從復(fù)制單節(jié)點(diǎn) Redis 有兩個(gè)痛點(diǎn)單點(diǎn)故障掛了就全完和讀壓力集中。主從復(fù)制讓一個(gè) Master 把數(shù)據(jù)同步給多個(gè) Slave讀寫分離Master 負(fù)責(zé)寫Slave 負(fù)責(zé)讀分?jǐn)倝毫?shù)據(jù)冗余Slave 是 Master 的備份復(fù)制方式首次是全量復(fù)制RDB 快照之后是增量復(fù)制命令流。注意默認(rèn) Slave 是只讀的不能在從節(jié)點(diǎn)上寫數(shù)據(jù)。3.2 實(shí)驗(yàn)搭建一主兩從① 配置主節(jié)點(diǎn)node1[rootredis-node1 ~]# vim /etc/redis/redis.conf#bind 127.0.0.1 -::1bind* -::* protected-mode no[rootredis-node1 ~]# systemctl restart redis_6379.service② 配置兩個(gè)從節(jié)點(diǎn)node2、node3# 在 redis-node2 節(jié)點(diǎn)[rootredis-node2 ~]# vim /etc/redis/redis.conf#bind 127.0.0.1 -::1bind* -::* protected-mode no replicaof172.25.254.106379# 指向主節(jié)點(diǎn) IP 端口[rootredis-node2 ~]# systemctl restart redis_6379.service# 在 redis-node3 節(jié)點(diǎn)[rootredis-node3 ~]# vim /etc/redis/redis.conf#bind 127.0.0.1 -::1bind* -::* protected-mode no replicaof172.25.254.106379[rootredis-node3 ~]# systemctl restart redis_6379.service3.3 驗(yàn)證復(fù)制狀態(tài)主節(jié)點(diǎn)查看node1[rootredis-node1 ~]# redis-cli127.0.0.1:6379info replication# Replicationrole:master connected_slaves:2 slave0:ip172.25.254.20,port6379,stateonline,offset391,lag0slave1:ip172.25.254.30,port6379,stateonline,offset391,lag1master_replid:e5f5cddc017ab0d5223213592d0482b832d3af77 master_repl_offset:391看到role:master且connected_slaves:2兩個(gè) slave 都是online說(shuō)明主從建立成功。從節(jié)點(diǎn)查看node2[rootredis-node2 ~]# redis-cli127.0.0.1:6379info replication# Replicationrole:slave master_host:172.25.254.10 master_port:6379 master_link_status:up# 與主節(jié)點(diǎn)連接正常slave_read_only:1# 從節(jié)點(diǎn)只讀3.4 測(cè)試數(shù)據(jù)同步 從節(jié)點(diǎn)只讀在主節(jié)點(diǎn)寫數(shù)據(jù)從節(jié)點(diǎn)立刻能讀到# 主節(jié)點(diǎn) node1 寫入[rootredis-node1 ~]# redis-cli127.0.0.1:6379setname lee OK127.0.0.1:6379get namelee# 從節(jié)點(diǎn) node2 / node3 讀取[rootredis-node2 ~]# redis-cli127.0.0.1:6379get namelee嘗試在從節(jié)點(diǎn)寫入會(huì)被拒絕[rootredis-node3 ~]# redis-cli127.0.0.1:6379get namelee127.0.0.1:6379settest123(error)READONLY You cantwriteagainst areadonly replica.? 主從復(fù)制 讀寫分離驗(yàn)證通過(guò)。四、Redis 哨兵模式 Sentinel4.1主從的短板靠哨兵補(bǔ)主從復(fù)制解決了讀壓力和數(shù)據(jù)備份但Master 掛了沒(méi)人自動(dòng)頂上。哨兵Sentinel就是來(lái)干這活的——一組哨兵進(jìn)程專門盯著 Master發(fā)現(xiàn)它真掛了自動(dòng)把一個(gè) Slave 提升為新 Master并讓其他節(jié)點(diǎn)改認(rèn)新主。幾個(gè)關(guān)鍵概念監(jiān)控Monitoring哨兵持續(xù) ping 主從節(jié)點(diǎn)主觀下線sdown某個(gè)哨兵自己覺(jué)得節(jié)點(diǎn)連不上了客觀下線odown達(dá)到quorum個(gè)哨兵都認(rèn)為下線才確認(rèn)真掛了故障轉(zhuǎn)移failover選舉領(lǐng)頭哨兵 → 選一個(gè) Slave 提升 → 通知其余節(jié)點(diǎn)quorum認(rèn)定主節(jié)點(diǎn)下線的票數(shù)實(shí)驗(yàn)中設(shè)為 2即至少 2 個(gè)哨兵同意。4.2 實(shí)驗(yàn)搭建哨兵① 主節(jié)點(diǎn)復(fù)制并編輯哨兵配置[rootredis-node1 ~]# cd redis-7.4.8/[rootredis-node1 redis-7.4.8]# cp -p sentinel.conf /etc/redis/[rootredis-node1 ~]# vim /etc/redis/sentinel.confprotected-mode no# 關(guān)閉保護(hù)模式port26379# 哨兵監(jiān)聽端口daemonize no# 前臺(tái)運(yùn)行方便看日志pidfile /var/run/redis-sentinel.pid loglevel notice sentinel monitor mymaster172.25.254.1063792# 監(jiān)控 mymasterquorum2sentinel down-after-milliseconds mymaster10000# 10 秒連不上視為下線sentinel parallel-syncs mymaster1# 故障轉(zhuǎn)移后同時(shí)同步新主的 slave 數(shù)sentinel failover-timeout mymaster180000# 故障切換超時(shí) 3 分鐘?? 原參考文檔此處寫的是172.25.254.100應(yīng)為實(shí)際 master 的172.25.254.10本文已修正否則哨兵會(huì)監(jiān)控一個(gè)不存在的地址。② 從節(jié)點(diǎn)也關(guān)閉保護(hù)模式[rootredis-node2 ~]# vim /etc/redis/redis.confprotected-mode no[rootredis-node2 ~]# systemctl restart redis_6379.service[rootredis-node3 ~]# vim /etc/redis/redis.confprotected-mode no[rootredis-node3 ~]# systemctl restart redis_6379.service③ 把哨兵配置發(fā)給兩個(gè)從節(jié)點(diǎn)三臺(tái)一起啟動(dòng)[rootredis-node1 ~]# scp /etc/redis/sentinel.conf root172.25.254.20:/etc/redis/[rootredis-node1 ~]# scp /etc/redis/sentinel.conf root172.25.254.30:/etc/redis/# 三臺(tái)節(jié)點(diǎn)都執(zhí)行[rootredis-node1 ~]# redis-sentinel /etc/redis/sentinel.conf啟動(dòng)后日志里能看到哨兵發(fā)現(xiàn)了 master 和兩個(gè) slave以及彼此# monitor master mymaster 172.25.254.10 6379 quorum 2* slave slave172.25.254.20:6379... mymaster172.25.254.106379* slave slave172.25.254.30:6379... mymaster172.25.254.106379* sentinel sentinel 28f2f3da...172.25.254.2026379 mymaster... * sentinel sentinel89568283...172.25.254.3026379 mymaster...4.3 實(shí)驗(yàn)測(cè)試故障切換① 干掉主節(jié)點(diǎn)[rootredis-node1 ~]# redis-cli127.0.0.1:6379SHUTDOWN not connected② 觀察哨兵日志故障轉(zhuǎn)移過(guò)程# sdown master mymaster 172.25.254.10 6379 # 主觀下線# odown master mymaster 172.25.254.10 6379 #quorum 2/2 # 客觀下線# try-failover master mymaster ...# vote-for-leader d0780e7f... 1 # 投票選領(lǐng)頭哨兵# selected-slave slave 172.25.254.20:6379 ... # 選中 .20 提升# promoted-slave slave 172.25.254.20:6379 ... # 提升為 master# failover-end master mymaster 172.25.254.10 6379# switch-master mymaster 172.25.254.10 6379 172.25.254.20 6379 # 主切換到 .20③ 在 node3 上確認(rèn)已認(rèn)新主[rootredis-node3 ~]# redis-cli127.0.0.1:6379info replication# Replicationrole:slave master_host:172.25.254.20# 已經(jīng)指向新主 .20master_port:6379 master_link_status:up④ 恢復(fù)原主節(jié)點(diǎn)node1[rootredis-node1 ~]# /etc/init.d/redis_6379 start原主重啟后變成新主的從節(jié)點(diǎn)在 node2 上能看到[rootredis-node2 ~]# redis-cli127.0.0.1:6379info replication# Replicationrole:master connected_slaves:2 slave0:ip172.25.254.30,port6379,stateonline,... slave1:ip172.25.254.10,port6379,stateonline,...# 原主 .10 已成為從? 哨兵自動(dòng)故障轉(zhuǎn)移驗(yàn)證通過(guò)。五、Redis Cluster 集群5.1 理論集群解決了什么哨兵解決了主掛了自動(dòng)切換但它只有一個(gè) Master 寫寫能力和存儲(chǔ)容量都有上限。Redis Cluster 是分布式方案數(shù)據(jù)分片把數(shù)據(jù)按slot CRC16(key) % 16384分到 16384 個(gè)哈希槽槽平均分配到多個(gè) Master無(wú)中心化客戶端連任意節(jié)點(diǎn)節(jié)點(diǎn)算槽后命中自己就處理否則返回MOVED重定向內(nèi)置高可用每個(gè) Master 配 SlaveMaster 宕機(jī)其 Slave 自動(dòng)接管槽不需要哨兵在線擴(kuò)縮容槽可以在節(jié)點(diǎn)間遷移集群不中斷。5.2 實(shí)驗(yàn)環(huán)境6 臺(tái)機(jī)器172.25.254.10 ~ 60其中.10/.20/.30為 Master.40/.50/.60為對(duì)應(yīng) Slave。5.3 配置集群節(jié)點(diǎn)6 臺(tái)節(jié)點(diǎn)都執(zhí)行以下配置先設(shè)主從認(rèn)證、開啟 cluster[rootredis-node1 ~]# vim /etc/redis/6379.confmasterauth123456# 集群主從認(rèn)證密碼cluster-enabledyes# 開啟 cluster 功能cluster-config-file nodes-6379.conf# 集群配置文件cluster-node-timeout15000# 節(jié)點(diǎn)超時(shí)ms[rootredis-node1 ~]# /etc/init.d/redis_6379 stop[rootredis-node1 ~]# /etc/init.d/redis_6379 start# 首次搭建先清掉舊數(shù)據(jù)已有數(shù)據(jù)時(shí)必須[rootredis-node1 ~]# redis-cli127.0.0.1:6379flushall127.0.0.1:6379CLUSTER RESET HARD5.4 創(chuàng)建集群一條命令拉起 3 主 3 從--cluster-replicas 1表示每個(gè) Master 配 1 個(gè) Slave[rootredis-node1 ~]# redis-cli --cluster create \172.25.254.10:6379172.25.254.20:6379172.25.254.30:6379\172.25.254.40:6379172.25.254.50:6379172.25.254.60:6379\--cluster-replicas1輸出關(guān)鍵片段自動(dòng)分配槽位和主從Master[0] - Slots 0 - 5460 Master[1] - Slots 5461 - 10922 Master[2] - Slots 10923 - 16383 Adding replica 172.25.254.50:6379 to 172.25.254.10:6379 Adding replica 172.25.254.60:6379 to 172.25.254.20:6379 Adding replica 172.25.254.40:6379 to 172.25.254.30:6379 Can I set the above configuration? (type yes to accept): yes Nodes configuration updated [OK] All nodes agree about slots configuration.5.5 驗(yàn)證集群狀態(tài)# 集群信息總覽[rootredis-node1 ~]# redis-cli --cluster info 172.25.254.10:6379172.25.254.10:6379 -0keys|5461slots|1slaves.172.25.254.30:6379 -0keys|5461slots|1slaves.172.25.254.20:6379 -0keys|5462slots|1slaves.[OK]0keysin3masters.# 集群自身狀態(tài)[rootredis-node1 ~]# redis-cli cluster infocluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_known_nodes:6 cluster_size:3# 健康檢查[rootredis-node1 ~]# redis-cli --cluster check 172.25.254.10:6379[OK]All16384slots covered.cluster_state:ok且All 16384 slots covered說(shuō)明集群健康。拓?fù)淙缦?.6集群擴(kuò)容加一主一從業(yè)務(wù)增長(zhǎng)要加機(jī)器集群支持在線擴(kuò)容。① 加入新主節(jié)點(diǎn) .70[rootredis-node1 ~]# redis-cli --cluster add-node 172.25.254.70:6379 172.25.254.10:6379[rootredis-node1 ~]# redis-cli --cluster check 172.25.254.10:6379172.25.254.70:6379 -0keys|0slots|0slaves.# 新主還沒(méi)槽② 遷移槽給新主reshard 4096 個(gè)[rootredis-node1 ~]# redis-cli --cluster reshard 172.25.254.10:6379How many slotsdoyou want to move(from1to16384)?4096What is the receivingnodeID? dfabfe07170ac9b5d20a5a7a70c836877bd64504# .70 的 IDPlease enter all thesourcenodeIDs. Sourcenode#1: all # 從所有現(xiàn)有 master 取槽Sourcenode#2: doneReady to move4096slots.遷移后.70分到 4096 個(gè)槽從三個(gè)老 master 各取一部分M: dfabfe07170ac9b5d20a5a7a70c836877bd64504 172.25.254.70:6379 slots:[0-1364],[5461-6826],[10923-12287] (4096 slots) master③ 給新主掛從節(jié)點(diǎn) .80[rootredis-node1 ~]# redis-cli --cluster add-node 172.25.254.80:6379 172.25.254.10:6379 \--cluster-slave --cluster-master-id dfabfe07170ac9b5d20a5a7a70c836877bd64504擴(kuò)容后變成4 主 4 從槽自動(dòng)均攤到每主 4096 個(gè)。5.7集群縮容刪一主一從① 把 .70 的槽遷回 .10[rootredis-node1 ~]# redis-cli --cluster reshard 172.25.254.10:6379How many slotsdoyou want to move(from1to16384)?4096What is the receivingnodeID? 8db833f3c3bc6b8f93e87111f13f56d366f833a0# .10 的 IDSourcenode#1: dfabfe07170ac9b5d20a5a7a70c836877bd64504 # .70 的 IDSourcenode#2: done② 刪除節(jié)點(diǎn)先刪從 .80再刪主 .70[rootredis-node1 ~]# redis-cli --cluster del-node 172.25.254.10:6379 1176ee294e6b5071ca57e93374d04ac22028daedSending CLUSTER FORGET messages to the cluster...[rootredis-node1 ~]# redis-cli --cluster del-node 172.25.254.10:6379 dfabfe07170ac9b5d20a5a7a70c836877bd64504[rootredis-node1 ~]# redis-cli --cluster check 172.25.254.10:6379[OK]All16384slots covered.縮容完成恢復(fù)為 3 主 3 從。?? 刪除主節(jié)點(diǎn)前必須先把它的槽全部遷走否則會(huì)丟數(shù)據(jù)。六、三種高可用方案對(duì)比小結(jié)方案寫能力存儲(chǔ)上限自動(dòng)故障轉(zhuǎn)移是否需要哨兵適用場(chǎng)景主從復(fù)制單 Master單節(jié)點(diǎn)? 手動(dòng)否讀多寫少、要備份哨兵 Sentinel單 Master單節(jié)點(diǎn)? 自動(dòng)是高可用、寫量不大Cluster 集群多 Master橫向擴(kuò)展? 自動(dòng)否內(nèi)置大數(shù)據(jù)量、高并發(fā)寫七、總結(jié)Redis 是什么基于內(nèi)存的 KV 數(shù)據(jù)庫(kù)快、支持持久化、數(shù)據(jù)類型豐富主從復(fù)制一主多從做讀寫分離和數(shù)據(jù)備份但 Master 掛了要人手動(dòng)頂哨兵模式哨兵自動(dòng)監(jiān)控 故障轉(zhuǎn)移解決主從的單點(diǎn)問(wèn)題但仍只有一個(gè) Master 寫Cluster 集群數(shù)據(jù)分 16384 槽、多 Master 分片既能橫向擴(kuò)容又能自動(dòng)故障轉(zhuǎn)移是大規(guī)模場(chǎng)景的終極方案持久化RDB AOF 都開重啟優(yōu)先 AOFRDB 做備份。