?1 概要介紹
1.1 應(yīng)用場(chǎng)景
緩存:相對(duì)靜態(tài)、或變化緩慢的數(shù)據(jù),可以利用緩存降低數(shù)據(jù)庫IO壓力,提升性能
分布式鎖:集群環(huán)境下對(duì)同一個(gè)資源的競(jìng)爭(zhēng),可借助redis來實(shí)現(xiàn)對(duì)資源加鎖??刹殚唕edis官方redlock相關(guān)介紹
秒殺:在redis中進(jìn)行預(yù)處理,充當(dāng)緩沖,將處理結(jié)果延遲持久化到數(shù)據(jù)庫。規(guī)避高并發(fā)對(duì)DB的壓力
計(jì)算器:每次操作加1,redis天然支持計(jì)數(shù)的api操作
消息隊(duì)列:功能太簡(jiǎn)單,一般不用
分布式session
限數(shù):每60秒獲取一次驗(yàn)證碼,設(shè)置key、value、失效時(shí)間1分鐘到redis,每次獲取校驗(yàn)碼進(jìn)行先驗(yàn)證redis是否有值即可
防爬蟲緩存穿透:
請(qǐng)求透過緩存層,直接命中DB,并發(fā)量大,造成DB層宕機(jī)
造成緩存穿透的基本原因有兩個(gè)。第一,自身業(yè)務(wù)代碼或者數(shù)據(jù)出現(xiàn)問 題,第二,一些惡意攻擊、爬蟲等造成大量空命中。 解決方案:在redis中保存失效期較短的空緩存。
1.2 不適合做什么
不適合用來做長(zhǎng)期持久化數(shù)據(jù)
不適合用來做大數(shù)據(jù)量的頻繁存取,容易阻塞。適合短頻快的數(shù)據(jù)存取。
1.3數(shù)據(jù)結(jié)構(gòu)
key-value鍵值對(duì),value的數(shù)據(jù)類型包括:string、hash、set、zset(有序集合)、list等。redis是用C語言編寫的,如果對(duì)數(shù)據(jù)結(jié)構(gòu)與算法還有印象的同學(xué)應(yīng)該很親切。
常用的string 與hash,學(xué)會(huì)運(yùn)用這兩類即可。
1.4單線程架構(gòu)
redis接收客戶端的請(qǐng)求,把執(zhí)行命令插入一個(gè)隊(duì)列中,然后逐個(gè)被執(zhí)行。因?yàn)槭菃尉€程架構(gòu),所以不會(huì)有兩個(gè)命令在同一時(shí)刻執(zhí)行,redis他自身是線程安全的,這個(gè)特性很重要。
單線程為什么還這么快?redis的命令操作都是在內(nèi)存中進(jìn)行,可以在納秒級(jí)別就處理完,使用了epoll非阻塞IO(想深入了解可以去查閱網(wǎng)上資料),單線程也就省卻了線程切換的CPU消耗。
了解單線程模型有助于我們做出更好的部署決策,如果在一臺(tái)多核主機(jī)上部署一個(gè)實(shí)例,太浪費(fèi)CPU資源了。

?
2 架構(gòu)原理
redis官方集群模式包括:?jiǎn)喂?jié)點(diǎn)、一主一從,一主多從、哨兵模式、cluster模式等。開源方案codis實(shí)現(xiàn)的代理模式等等。
其他模式的優(yōu)缺點(diǎn)這里就不講了,這里講講cluster模式的優(yōu)點(diǎn):
高性能:客戶端直連模式,沒有代理轉(zhuǎn)發(fā)。redis自身處理客戶端請(qǐng)求飛快,納秒級(jí)別
高并發(fā):普通的硬件配置,單節(jié)點(diǎn)可支持10WQPS
高可用:?jiǎn)蝹€(gè)主節(jié)點(diǎn)宕機(jī)不影響整體集群的服務(wù)能力,可通過自動(dòng)健康檢查、故障轉(zhuǎn)移將從節(jié)點(diǎn)升級(jí)為主節(jié)點(diǎn)
可彈性擴(kuò)展:在線水平擴(kuò)展數(shù)據(jù)容量、吞吐量、主從節(jié)點(diǎn)個(gè)數(shù)等
2.1 redis cluster方案架構(gòu)圖

?2.2 方案說明
以上圖所示的三主三從集群為例,每個(gè)主節(jié)點(diǎn)處理各自的數(shù)據(jù),提供讀寫能力,從節(jié)點(diǎn)異步復(fù)制主節(jié)點(diǎn)的數(shù)據(jù)。假設(shè)給每個(gè)redis實(shí)例分配了8G的最大內(nèi)存,總的數(shù)據(jù)容量大小為24G(如果想繼續(xù)擴(kuò)充數(shù)據(jù)容量,繼續(xù)加主節(jié)點(diǎn))。單個(gè)redis實(shí)例的最大內(nèi)存不建議超過10G。
2.3 數(shù)據(jù)分布
cluster集群方案,采用的是虛擬槽分區(qū),槽范圍是0-16383,有16384個(gè)槽。集群中有3個(gè)主節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)大致負(fù)責(zé)5500個(gè)槽的讀寫,節(jié)點(diǎn)會(huì)維護(hù)自身負(fù)責(zé)的虛擬槽。
鍵所對(duì)應(yīng)的哈希值通過如下公式計(jì)算:CRC16(key)%16384。目前常見的redisson、jedis等客戶端工具都已實(shí)現(xiàn),推薦使用redisson,jedis可以棄療。
redisson客戶端初始化的時(shí)候,會(huì)加載集群的元數(shù)據(jù)信息,創(chuàng)建連接池。包括IP 端口 哈希槽的映射關(guān)系。
在實(shí)際使用時(shí),set key value命令會(huì)計(jì)算hash值,把key-value設(shè)置到對(duì)應(yīng)的主節(jié)點(diǎn)上。
?2.4 數(shù)據(jù)持久化 RDB VS AOF
?2.4.1 RDB全量持久化

父進(jìn)程執(zhí)行fork操作創(chuàng)建子進(jìn)程,fork操作過程中父進(jìn)程會(huì)阻塞,阻塞的時(shí)間跟所用內(nèi)存大小成正比,要求redis實(shí)例最大不要超過10G。由子進(jìn)程去創(chuàng)建最新的RDB文件。
什么時(shí)候會(huì)發(fā)生持久化操作呢?1、人工執(zhí)行bgsave命令;2、達(dá)到配置文件中指定的約束條件
rdb文件放置在哪里? dbfilename 配置項(xiàng)指定
RDB的優(yōu)點(diǎn):redis重啟恢復(fù)數(shù)據(jù)的時(shí)候速度很快
RDB的缺點(diǎn): 無法實(shí)時(shí)持久化,fork會(huì)阻塞父進(jìn)程
關(guān)閉RDB配置 :save “”
2.4.2 AOF增量持久化
默認(rèn)是關(guān)閉的,配置 appendonly yes 開啟。appendfilename 指定文件名,保存的路徑與RDB一樣,通過dir指定
所有的操作命令會(huì)追加到aof_buf緩沖區(qū),根據(jù)策略appendsync everysec 每秒同步到磁盤上的AOF文件。
aof文件重寫壓縮過程中也會(huì)執(zhí)行fork操作。
redis重啟時(shí)可以加載aof文件進(jìn)行數(shù)據(jù)恢復(fù)。
RDB與AOF各有優(yōu)劣,根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景選擇合適的方案,甚至不用。
2.4.3 ?redis啟動(dòng)過程的數(shù)據(jù)恢復(fù)

2.4 集群通信
每個(gè)節(jié)點(diǎn)會(huì)隨機(jī)對(duì)集群中的部分節(jié)點(diǎn)發(fā)生ping命令,判斷其他節(jié)點(diǎn)的健康狀態(tài),
3 集群安裝與配置
3.1 關(guān)鍵配置
protected-mode no 允許外部主機(jī)訪問
cluster-enabled yes ?配置為cluster 模式
cluster-config-file nodes-6379.conf? 集群節(jié)點(diǎn)配置信息,包括nodeid,集群信息。此文件非常關(guān)鍵,要確保故障轉(zhuǎn)移或者重啟的時(shí)候此文件還在,所以如果在docker環(huán)境下要外掛到外部存儲(chǔ)
cluster-node-timeout 2000 ?節(jié)點(diǎn)連接超時(shí),如果集群規(guī)模小,都在同一個(gè)網(wǎng)絡(luò)環(huán)境下,可以配置的短些,更快的做故障轉(zhuǎn)移
slowlog-log-slower-than ?慢查詢?nèi)罩?,用于性能分析,生產(chǎn)環(huán)境可設(shè)置為1000(微妙)
slowlog-max-len ? 保存慢查詢的隊(duì)列長(zhǎng)度 ,設(shè)置為1000
maxclients ?集群支持的最大連接 50000
cluster-slave-validity-factor?設(shè)置為0,如果master slave都掛掉,slave跟master失聯(lián)又超過這個(gè)數(shù)值*timeout的數(shù)值,就不會(huì)發(fā)起選舉了。如果設(shè)置為0,就是永遠(yuǎn)都會(huì)嘗試發(fā)起選舉,嘗試從slave變?yōu)閙ater
cluster-require-full-coverage??設(shè)置為no,默認(rèn)為yes,故障發(fā)現(xiàn)到自動(dòng) 完成轉(zhuǎn)移期間整個(gè)集群是不可用狀態(tài),對(duì)于大多數(shù)業(yè)務(wù)無法容忍這種情況, 因此要設(shè)置為no,當(dāng)主節(jié)點(diǎn)故障時(shí)只影 響它負(fù)責(zé)槽的相關(guān)命令執(zhí)行,不會(huì)影響其他主節(jié)點(diǎn)的可用性
save “” ?通用配置,關(guān)閉RDB持久化,只使用AOF。
maxmemory 8GBRedis默認(rèn)無限使用服務(wù)器內(nèi)存,為防止極端情況下導(dǎo)致系統(tǒng)內(nèi)存耗 盡,建議所有的Redis進(jìn)程都要配置maxmemory,要保證機(jī)器本身有30%左右的閑置內(nèi)存
maxmemory-policy ?volatile-lru?內(nèi)存剔除策略
logfile “”? 默認(rèn)為空,則在控制臺(tái)打印,否則輸出日志到指定的log文件
3.2 注意事項(xiàng)
操作系統(tǒng)本身的最大連接數(shù)設(shè)置open ?files建議設(shè)置為65535,redis的最大連接數(shù)受此限制。
ulimit -a 可以查看所有配置
修改: vim /etc/security/limits.conf
主節(jié)點(diǎn)平均分配到不同的機(jī)器上,否則容易造成單點(diǎn)故障以及復(fù)制風(fēng)暴。
操作系統(tǒng)關(guān)閉THP:
vim /etc/rc.d/rc.local
增加下列內(nèi)容:
if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
echo never > /sys/kernel/mm/transparent_hugepage/defrag
fi
然后給rc.local添加可執(zhí)行權(quán)限:chmod +x /etc/rc.d/rc.local。重啟生效
slave-read-only集群模式下無效,cluster集群模式下,從節(jié)點(diǎn)默認(rèn)是處于冷備的狀態(tài),不提供讀寫服務(wù)。需要客戶端去開啟從節(jié)點(diǎn)的readonly,推薦用redisson java客戶端工具,jedis不支持。
redis總共16個(gè)數(shù)據(jù)庫,默認(rèn)使用第0個(gè)數(shù)據(jù)庫,集群模式下只能用第0數(shù)據(jù)庫。多數(shù)據(jù)庫功能就算單節(jié)點(diǎn)部署方案也不建議使用,這點(diǎn)知道即可。
3.3 修改linux連接數(shù)限制
切換到root用戶修改配置sysctl.conf?
vim /etc/sysctl.conf
添加配置:
vm.max_map_count=655360
vm.overcommit_memory=1
net.core.somaxconn= 1024
vim /etc/security/limits.conf?
添加
* soft?nofile?65536
* hard?nofile?65536
* soft nproc 65536
* hard nproc 65536
vi /etc/security/limits.d/20-nproc.conf?
?#加大普通用戶限制 也可以改為unlimited
?* soft nproc 40960?
?root soft nproc unlimited
reboot或者重新登錄
3.4 linux環(huán)境下安裝
wget?http://download.redis.io/releases/redis-4.0.7.tar.gz
tar xzf?redis-4.0.7.tar.gz
cd?redis-4.0.7
yum -y install gcc
編譯安裝:make install?
Redis編譯安裝之后,src和/usr/local/bin目錄下多了幾個(gè)以redis開頭可執(zhí)行文 件
啟動(dòng)redis:
cd? /redis/data? 當(dāng)前工作目錄是默認(rèn)的根目錄
redis-server ?/redis/data/redis.conf ??
?配置文件方式啟動(dòng)方式,也是推薦的方案,將定制的配置文件放在/redis/data目錄,
?/redis/data此時(shí)就是根目錄
停止redis:redis-cli shutdown ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?不要用kill,太粗暴,可能導(dǎo)致數(shù)據(jù)丟失
集群至少需要6節(jié)點(diǎn),3主3從,準(zhǔn)備至少6個(gè)節(jié)點(diǎn)。
3.4.1?啟動(dòng)節(jié)點(diǎn)
啟動(dòng)6個(gè)節(jié)點(diǎn):redis-server redis.conf?,確保該目錄下沒有AOF RDB文件,否則不能加入集群。

3.4.2?節(jié)點(diǎn)握手
? 節(jié)點(diǎn)彼此之間通過gossip協(xié)議(“謠言”協(xié)議了解一下),一個(gè)節(jié)點(diǎn)連接其他5個(gè)節(jié)點(diǎn)即可。
127.0.0.1:6379>cluster meet 127.0.0.1 6382?
>cluster ?nodes ?查看集群信息
>cluster info ?查看集群狀態(tài),此時(shí)不可用
3.4.3? 分配槽
redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0...5461}
3.4.4 分配子節(jié)點(diǎn)
127.0.0.1:6382>cluster replicate cfb28ef1deee4e0fa78da86abe5d24566744411e?
3.5 借助redis-trib工具進(jìn)行自動(dòng)化創(chuàng)建集群
手工創(chuàng)建集群過程很繁瑣。官方提供了redis-trib.rb工具來實(shí)現(xiàn)集群管理。
3.5.1 ruby環(huán)境準(zhǔn)備
-- 下載ruby?
wget https:// cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz?
-- 安裝ruby?
tar xvf ruby-2.3.1.tar.gz?
cd??ruby-2.3.1
./configure -prefix=/usr/local/ruby?
make
make install?
cd /usr/local/ruby?
sudo cp bin/ruby /usr/local/bin?
sudo cp bin/gem /usr/local/bin
--安裝rubygem redis依賴
wget http:// rubygems.org/downloads/redis-3.3.0.gem?
gem install -l redis-3.3.0.gem?
如果提示缺少zlib ,
進(jìn)入ruby項(xiàng)目文件夾:
#cd ext/zlib
#ruby ./extconf.rb
#make?
#make install
---安裝redis-trib.rb
sudo cp /{redis_home}/src/redis-trib.rb /usr/local/bin
--測(cè)試是否安裝成功
>redis-trib.rb?
3.5.2 啟動(dòng)6個(gè)節(jié)點(diǎn)
3.5.3 創(chuàng)建集群
redis-trib.rb create --replicas 1 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483 127.0.0.1:6484 127.0.0.1:6485 127.0.0.1:6486
出現(xiàn)提示創(chuàng)建3主3從集群的時(shí)候,輸入yes即可。
3.6 腳本自動(dòng)化
運(yùn)維人員可以將腳本做一些完善,在linux下自動(dòng)化創(chuàng)建安裝redis節(jié)點(diǎn),自動(dòng)化創(chuàng)建集群
4 集群在線擴(kuò)容
1)準(zhǔn)備新節(jié)點(diǎn)
2)?加入集群?
3)遷移槽和數(shù)據(jù)
在生產(chǎn)環(huán)境可以用redis-trib.rb工具來做集群擴(kuò)容,做到半自動(dòng)化,建議運(yùn)維人員在測(cè)試環(huán)境先摸透整個(gè)流程。
添加節(jié)點(diǎn):
redis-trib.rb add-node 127.0.0.1:6385 127.0.0.1:6379?
遷移槽:
1、redis-trib.rb reshard 10.42.202.243:6000 ? #任意指定一個(gè)節(jié)點(diǎn),以便獲取集群信息
2、接下來要你輸入需要遷移的槽數(shù)量 :2000
3、輸入目標(biāo)節(jié)點(diǎn)的nodeid
4、輸入源節(jié)點(diǎn)的ID ,要是主節(jié)點(diǎn)的id,輸入done表示結(jié)束
5、輸入yes,開搞吧
6、為新增的主節(jié)點(diǎn)添加slave:
redis-trib.rb add-node --slave ip:port masterip:port
5 故障轉(zhuǎn)移原理
5.1 集群內(nèi)部通信
集群內(nèi)部采用gossip協(xié)議進(jìn)行通信,節(jié)點(diǎn)之間交換彼此的信息,使用了單獨(dú)的TCP端口,默認(rèn)是6379+10000。詳細(xì)過程請(qǐng)閱讀“redis開發(fā)與運(yùn)維.PDF”

5.2 故障發(fā)現(xiàn)
當(dāng)集群少量節(jié)點(diǎn)出行故障時(shí),能通過自動(dòng)化故障轉(zhuǎn)移保證集群的高可用。那是怎么發(fā)現(xiàn)故障節(jié)點(diǎn)的呢?
包括兩個(gè)環(huán)節(jié):主觀下線pfail (單個(gè)節(jié)點(diǎn)認(rèn)為另一個(gè)節(jié)點(diǎn)下線,將它標(biāo)記為pfail) 、客觀下線 fail(節(jié)點(diǎn)彼此之間通過信息交換,大家達(dá)成共識(shí)了,都認(rèn)為該節(jié)點(diǎn)下線,標(biāo)記為fail)
這里所說的“大家達(dá)成共識(shí)了”,指的是主節(jié)點(diǎn)投票超過半數(shù)以上,就是說如果是3主3從集群,至少要有2個(gè)主節(jié)點(diǎn)認(rèn)為該節(jié)點(diǎn)下線,從節(jié)點(diǎn)沒資格參與投票。
如果是主節(jié)點(diǎn),就要進(jìn)行故障轉(zhuǎn)移了
5.3 故障轉(zhuǎn)移?
主節(jié)點(diǎn)發(fā)生故障了,從節(jié)點(diǎn)收到fail廣播消息,從節(jié)點(diǎn)會(huì)嘗試發(fā)起選舉
其他主節(jié)點(diǎn)接收到選舉消息,會(huì)進(jìn)行投票,超過半數(shù)以上通過才可以完成選舉。
(詳細(xì)過程請(qǐng)閱讀redis開發(fā)與運(yùn)維.pdf)
故障主節(jié)點(diǎn)也算在投票數(shù)內(nèi),假設(shè)集群內(nèi)節(jié)點(diǎn)規(guī)模是3主3從,其中有2 個(gè)主節(jié)點(diǎn)部署在一臺(tái)機(jī)器上,當(dāng)這臺(tái)機(jī)器宕機(jī)時(shí),由于從節(jié)點(diǎn)無法收集到 3/2+1個(gè)主節(jié)點(diǎn)選票將導(dǎo)致故障轉(zhuǎn)移失敗。這個(gè)問題也適用于故障發(fā)現(xiàn)環(huán) 節(jié)。因此部署集群時(shí)所有主節(jié)點(diǎn)最少需要部署在3臺(tái)物理機(jī)上才能避免單點(diǎn) 問題。
6 集群運(yùn)維注意事項(xiàng)
? 在上線之前進(jìn)行故障轉(zhuǎn)移測(cè)試,及時(shí)發(fā)現(xiàn)問題,鍛煉掌握集群運(yùn)維能力
?學(xué)會(huì)從cpu ?網(wǎng)絡(luò) 存儲(chǔ) 日志各個(gè)層面進(jìn)行日常運(yùn)維
6.1 常用命令
redis-cli -p 6000 -h host?
redis-cli -p 6000 shutdown
cluster nodes??
6.2 基準(zhǔn)測(cè)試工具
redis-benchmark -h 11.4.74.44? -p 6000? -c 100 -n 20000
基準(zhǔn)測(cè)試的數(shù)據(jù)由于key value? 大小 網(wǎng)絡(luò)因素等,并不能直接等同于生產(chǎn)環(huán)境的qps,但是可以做大致參考。
6.3 運(yùn)維案例
用戶報(bào)的一個(gè)故障:
org.redisson.client.RedisException:?ERR?max?number?of?clients?reached.?channel:?[id:?0x136f239c,?L:/11.13.49.91:34666?-?R:/11.4.74.47:6000]?command:?(READONLY),?params:?[]
排查過程:首先確認(rèn)內(nèi)核配置無誤
/etc/sysctl.conf ?fs.file-max=65535
ulimit -n ?最大文件描述符? ?65536
redis.conf配置:max clients 50000 ? ?timeout ?0
使用redis-cli工具:info ?clients查看當(dāng)前客戶端連接數(shù) ?,發(fā)現(xiàn)快占滿了,接近5w,wtf這怎么可能。。
查看所有client:client list
查看timeout配置:config get timeout? ? 0 ,這是問題根源之一
臨時(shí)性設(shè)置timeout:config set timeout 150
monitor命令可以顯示當(dāng)前redis執(zhí)行的命令,發(fā)現(xiàn)同一個(gè)ip有很多重復(fù)性的占用連接。
反查這些ip實(shí)例的日志,發(fā)現(xiàn)應(yīng)用程序不能正常啟動(dòng),一直重啟,導(dǎo)致一直重復(fù)創(chuàng)建連接,又缺乏超時(shí)釋放的策略
先干掉不能啟動(dòng)的幾個(gè)應(yīng)用實(shí)例。
解決方案:一定要設(shè)置redis.conf的timeout 60,不能讓空閑連接占著茅坑不拉屎,把所有連接耗盡。容器一定要配置健康檢查,如果發(fā)現(xiàn)反復(fù)重啟的,要格外關(guān)注。
舉一反三:rocketmq、kafka、es呢
7 ?針對(duì)開發(fā)人員
?7.1 熟悉redis架構(gòu)原理
別拿他當(dāng)黑盒用,了解哪些操作會(huì)導(dǎo)致redis阻塞。否則就是給自己挖坑
7.2 使用redisson 而不是jedis
很傻瓜化的一個(gè)java客戶端,磨刀不誤砍柴工,多查看官網(wǎng)api文檔,如果你用的很辛苦,那可能是你的姿勢(shì)不對(duì)
?7.3 redis使用規(guī)范
在實(shí)際生產(chǎn)環(huán)境中,不可能給每個(gè)應(yīng)用都建一套redis集群,一般是按業(yè)務(wù)領(lǐng)域分。
為了防止key沖突,各個(gè)應(yīng)用直接約定key值加上約定的前綴來區(qū)分。
7.4 批量設(shè)置
在cluster模式下,對(duì)mset? mget命令限制很多,要求批量設(shè)置的key 都在同一臺(tái)redis實(shí)例上,否則報(bào)異常。
有什么替代方案呢?用hashmap
