?1 概要介紹
1.1 應用場景
緩存:相對靜態(tài)、或變化緩慢的數(shù)據(jù),可以利用緩存降低數(shù)據(jù)庫IO壓力,提升性能
分布式鎖:集群環(huán)境下對同一個資源的競爭,可借助redis來實現(xiàn)對資源加鎖。可查閱redis官方redlock相關介紹
秒殺:在redis中進行預處理,充當緩沖,將處理結(jié)果延遲持久化到數(shù)據(jù)庫。規(guī)避高并發(fā)對DB的壓力
計算器:每次操作加1,redis天然支持計數(shù)的api操作
消息隊列:功能太簡單,一般不用
分布式session
限數(shù):每60秒獲取一次驗證碼,設置key、value、失效時間1分鐘到redis,每次獲取校驗碼進行先驗證redis是否有值即可
防爬蟲緩存穿透:
請求透過緩存層,直接命中DB,并發(fā)量大,造成DB層宕機
造成緩存穿透的基本原因有兩個。第一,自身業(yè)務代碼或者數(shù)據(jù)出現(xiàn)問 題,第二,一些惡意攻擊、爬蟲等造成大量空命中。 解決方案:在redis中保存失效期較短的空緩存。
1.2 不適合做什么
不適合用來做長期持久化數(shù)據(jù)
不適合用來做大數(shù)據(jù)量的頻繁存取,容易阻塞。適合短頻快的數(shù)據(jù)存取。
1.3數(shù)據(jù)結(jié)構
key-value鍵值對,value的數(shù)據(jù)類型包括:string、hash、set、zset(有序集合)、list等。redis是用C語言編寫的,如果對數(shù)據(jù)結(jié)構與算法還有印象的同學應該很親切。
常用的string 與hash,學會運用這兩類即可。
1.4單線程架構
redis接收客戶端的請求,把執(zhí)行命令插入一個隊列中,然后逐個被執(zhí)行。因為是單線程架構,所以不會有兩個命令在同一時刻執(zhí)行,redis他自身是線程安全的,這個特性很重要。
單線程為什么還這么快?redis的命令操作都是在內(nèi)存中進行,可以在納秒級別就處理完,使用了epoll非阻塞IO(想深入了解可以去查閱網(wǎng)上資料),單線程也就省卻了線程切換的CPU消耗。
了解單線程模型有助于我們做出更好的部署決策,如果在一臺多核主機上部署一個實例,太浪費CPU資源了。
?
2 架構原理
redis官方集群模式包括:單節(jié)點、一主一從,一主多從、哨兵模式、cluster模式等。開源方案codis實現(xiàn)的代理模式等等。
其他模式的優(yōu)缺點這里就不講了,這里講講cluster模式的優(yōu)點:
高性能:客戶端直連模式,沒有代理轉(zhuǎn)發(fā)。redis自身處理客戶端請求飛快,納秒級別
高并發(fā):普通的硬件配置,單節(jié)點可支持10WQPS
高可用:單個主節(jié)點宕機不影響整體集群的服務能力,可通過自動健康檢查、故障轉(zhuǎn)移將從節(jié)點升級為主節(jié)點
可彈性擴展:在線水平擴展數(shù)據(jù)容量、吞吐量、主從節(jié)點個數(shù)等
2.1 redis cluster方案架構圖
?2.2 方案說明
以上圖所示的三主三從集群為例,每個主節(jié)點處理各自的數(shù)據(jù),提供讀寫能力,從節(jié)點異步復制主節(jié)點的數(shù)據(jù)。假設給每個redis實例分配了8G的最大內(nèi)存,總的數(shù)據(jù)容量大小為24G(如果想繼續(xù)擴充數(shù)據(jù)容量,繼續(xù)加主節(jié)點)。單個redis實例的最大內(nèi)存不建議超過10G。
2.3 數(shù)據(jù)分布
cluster集群方案,采用的是虛擬槽分區(qū),槽范圍是0-16383,有16384個槽。集群中有3個主節(jié)點,每個節(jié)點大致負責5500個槽的讀寫,節(jié)點會維護自身負責的虛擬槽。
鍵所對應的哈希值通過如下公式計算:CRC16(key)%16384。目前常見的redisson、jedis等客戶端工具都已實現(xiàn),推薦使用redisson,jedis可以棄療。
redisson客戶端初始化的時候,會加載集群的元數(shù)據(jù)信息,創(chuàng)建連接池。包括IP 端口 哈希槽的映射關系。
在實際使用時,set key value命令會計算hash值,把key-value設置到對應的主節(jié)點上。
?2.4 數(shù)據(jù)持久化 RDB VS AOF
?2.4.1 RDB全量持久化
父進程執(zhí)行fork操作創(chuàng)建子進程,fork操作過程中父進程會阻塞,阻塞的時間跟所用內(nèi)存大小成正比,要求redis實例最大不要超過10G。由子進程去創(chuàng)建最新的RDB文件。
什么時候會發(fā)生持久化操作呢?1、人工執(zhí)行bgsave命令;2、達到配置文件中指定的約束條件
rdb文件放置在哪里? dbfilename 配置項指定
RDB的優(yōu)點:redis重啟恢復數(shù)據(jù)的時候速度很快
RDB的缺點: 無法實時持久化,fork會阻塞父進程
關閉RDB配置 :save “”
2.4.2 AOF增量持久化
默認是關閉的,配置 appendonly yes 開啟。appendfilename 指定文件名,保存的路徑與RDB一樣,通過dir指定
所有的操作命令會追加到aof_buf緩沖區(qū),根據(jù)策略appendsync everysec 每秒同步到磁盤上的AOF文件。
aof文件重寫壓縮過程中也會執(zhí)行fork操作。
redis重啟時可以加載aof文件進行數(shù)據(jù)恢復。
RDB與AOF各有優(yōu)劣,根據(jù)實際業(yè)務場景選擇合適的方案,甚至不用。
2.4.3 ?redis啟動過程的數(shù)據(jù)恢復
2.4 集群通信
每個節(jié)點會隨機對集群中的部分節(jié)點發(fā)生ping命令,判斷其他節(jié)點的健康狀態(tài),
3 集群安裝與配置
3.1 關鍵配置
protected-mode no 允許外部主機訪問
cluster-enabled yes ?配置為cluster 模式
cluster-config-file nodes-6379.conf? 集群節(jié)點配置信息,包括nodeid,集群信息。此文件非常關鍵,要確保故障轉(zhuǎn)移或者重啟的時候此文件還在,所以如果在docker環(huán)境下要外掛到外部存儲
cluster-node-timeout 2000 ?節(jié)點連接超時,如果集群規(guī)模小,都在同一個網(wǎng)絡環(huán)境下,可以配置的短些,更快的做故障轉(zhuǎn)移
slowlog-log-slower-than ?慢查詢?nèi)罩荆糜谛阅芊治觯a(chǎn)環(huán)境可設置為1000(微妙)
slowlog-max-len ? 保存慢查詢的隊列長度 ,設置為1000
maxclients ?集群支持的最大連接 50000
cluster-slave-validity-factor?設置為0,如果master slave都掛掉,slave跟master失聯(lián)又超過這個數(shù)值*timeout的數(shù)值,就不會發(fā)起選舉了。如果設置為0,就是永遠都會嘗試發(fā)起選舉,嘗試從slave變?yōu)閙ater
cluster-require-full-coverage??設置為no,默認為yes,故障發(fā)現(xiàn)到自動 完成轉(zhuǎn)移期間整個集群是不可用狀態(tài),對于大多數(shù)業(yè)務無法容忍這種情況, 因此要設置為no,當主節(jié)點故障時只影 響它負責槽的相關命令執(zhí)行,不會影響其他主節(jié)點的可用性
save “” ?通用配置,關閉RDB持久化,只使用AOF。
maxmemory 8GBRedis默認無限使用服務器內(nèi)存,為防止極端情況下導致系統(tǒng)內(nèi)存耗 盡,建議所有的Redis進程都要配置maxmemory,要保證機器本身有30%左右的閑置內(nèi)存
maxmemory-policy ?volatile-lru?內(nèi)存剔除策略
logfile “”? 默認為空,則在控制臺打印,否則輸出日志到指定的log文件
3.2 注意事項
操作系統(tǒng)本身的最大連接數(shù)設置open ?files建議設置為65535,redis的最大連接數(shù)受此限制。
ulimit -a 可以查看所有配置
修改: vim /etc/security/limits.conf
主節(jié)點平均分配到不同的機器上,否則容易造成單點故障以及復制風暴。
操作系統(tǒng)關閉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í)行權限:chmod +x /etc/rc.d/rc.local。重啟生效
slave-read-only集群模式下無效,cluster集群模式下,從節(jié)點默認是處于冷備的狀態(tài),不提供讀寫服務。需要客戶端去開啟從節(jié)點的readonly,推薦用redisson java客戶端工具,jedis不支持。
redis總共16個數(shù)據(jù)庫,默認使用第0個數(shù)據(jù)庫,集群模式下只能用第0數(shù)據(jù)庫。多數(shù)據(jù)庫功能就算單節(jié)點部署方案也不建議使用,這點知道即可。
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目錄下多了幾個以redis開頭可執(zhí)行文 件
啟動redis:
cd? /redis/data? 當前工作目錄是默認的根目錄
redis-server ?/redis/data/redis.conf ??
?配置文件方式啟動方式,也是推薦的方案,將定制的配置文件放在/redis/data目錄,
?/redis/data此時就是根目錄
停止redis:redis-cli shutdown ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?不要用kill,太粗暴,可能導致數(shù)據(jù)丟失
集群至少需要6節(jié)點,3主3從,準備至少6個節(jié)點。
3.4.1?啟動節(jié)點
啟動6個節(jié)點:redis-server redis.conf?,確保該目錄下沒有AOF RDB文件,否則不能加入集群。
3.4.2?節(jié)點握手
? 節(jié)點彼此之間通過gossip協(xié)議(“謠言”協(xié)議了解一下),一個節(jié)點連接其他5個節(jié)點即可。
127.0.0.1:6379>cluster meet 127.0.0.1 6382?
>cluster ?nodes ?查看集群信息
>cluster info ?查看集群狀態(tài),此時不可用
3.4.3? 分配槽
redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0...5461}
3.4.4 分配子節(jié)點
127.0.0.1:6382>cluster replicate cfb28ef1deee4e0fa78da86abe5d24566744411e?
3.5 借助redis-trib工具進行自動化創(chuàng)建集群
手工創(chuàng)建集群過程很繁瑣。官方提供了redis-trib.rb工具來實現(xiàn)集群管理。
3.5.1 ruby環(huá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 ,
進入ruby項目文件夾:
#cd ext/zlib
#ruby ./extconf.rb
#make?
#make install
---安裝redis-trib.rb
sudo cp /{redis_home}/src/redis-trib.rb /usr/local/bin
--測試是否安裝成功
>redis-trib.rb?
3.5.2 啟動6個節(jié)點
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從集群的時候,輸入yes即可。
3.6 腳本自動化
運維人員可以將腳本做一些完善,在linux下自動化創(chuàng)建安裝redis節(jié)點,自動化創(chuàng)建集群
4 集群在線擴容
1)準備新節(jié)點
2)?加入集群?
3)遷移槽和數(shù)據(jù)
在生產(chǎn)環(huán)境可以用redis-trib.rb工具來做集群擴容,做到半自動化,建議運維人員在測試環(huán)境先摸透整個流程。
添加節(jié)點:
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 ? #任意指定一個節(jié)點,以便獲取集群信息
2、接下來要你輸入需要遷移的槽數(shù)量 :2000
3、輸入目標節(jié)點的nodeid
4、輸入源節(jié)點的ID ,要是主節(jié)點的id,輸入done表示結(jié)束
5、輸入yes,開搞吧
6、為新增的主節(jié)點添加slave:
redis-trib.rb add-node --slave ip:port masterip:port
5 故障轉(zhuǎn)移原理
5.1 集群內(nèi)部通信
集群內(nèi)部采用gossip協(xié)議進行通信,節(jié)點之間交換彼此的信息,使用了單獨的TCP端口,默認是6379+10000。詳細過程請閱讀“redis開發(fā)與運維.PDF”
5.2 故障發(fā)現(xiàn)
當集群少量節(jié)點出行故障時,能通過自動化故障轉(zhuǎn)移保證集群的高可用。那是怎么發(fā)現(xiàn)故障節(jié)點的呢?
包括兩個環(huán)節(jié):主觀下線pfail (單個節(jié)點認為另一個節(jié)點下線,將它標記為pfail) 、客觀下線 fail(節(jié)點彼此之間通過信息交換,大家達成共識了,都認為該節(jié)點下線,標記為fail)
這里所說的“大家達成共識了”,指的是主節(jié)點投票超過半數(shù)以上,就是說如果是3主3從集群,至少要有2個主節(jié)點認為該節(jié)點下線,從節(jié)點沒資格參與投票。
如果是主節(jié)點,就要進行故障轉(zhuǎn)移了
5.3 故障轉(zhuǎn)移?
主節(jié)點發(fā)生故障了,從節(jié)點收到fail廣播消息,從節(jié)點會嘗試發(fā)起選舉
其他主節(jié)點接收到選舉消息,會進行投票,超過半數(shù)以上通過才可以完成選舉。
(詳細過程請閱讀redis開發(fā)與運維.pdf)
故障主節(jié)點也算在投票數(shù)內(nèi),假設集群內(nèi)節(jié)點規(guī)模是3主3從,其中有2 個主節(jié)點部署在一臺機器上,當這臺機器宕機時,由于從節(jié)點無法收集到 3/2+1個主節(jié)點選票將導致故障轉(zhuǎn)移失敗。這個問題也適用于故障發(fā)現(xiàn)環(huán) 節(jié)。因此部署集群時所有主節(jié)點最少需要部署在3臺物理機上才能避免單點 問題。
6 集群運維注意事項
? 在上線之前進行故障轉(zhuǎn)移測試,及時發(fā)現(xiàn)問題,鍛煉掌握集群運維能力
?學會從cpu ?網(wǎng)絡 存儲 日志各個層面進行日常運維
6.1 常用命令
redis-cli -p 6000 -h host?
redis-cli -p 6000 shutdown
cluster nodes??
6.2 基準測試工具
redis-benchmark -h 11.4.74.44? -p 6000? -c 100 -n 20000
基準測試的數(shù)據(jù)由于key value? 大小 網(wǎng)絡因素等,并不能直接等同于生產(chǎn)環(huán)境的qps,但是可以做大致參考。
6.3 運維案例
用戶報的一個故障:
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:?[]
排查過程:首先確認內(nèi)核配置無誤
/etc/sysctl.conf ?fs.file-max=65535
ulimit -n ?最大文件描述符? ?65536
redis.conf配置:max clients 50000 ? ?timeout ?0
使用redis-cli工具:info ?clients查看當前客戶端連接數(shù) ?,發(fā)現(xiàn)快占滿了,接近5w,wtf這怎么可能。。
查看所有client:client list
查看timeout配置:config get timeout? ? 0 ,這是問題根源之一
臨時性設置timeout:config set timeout 150
monitor命令可以顯示當前redis執(zhí)行的命令,發(fā)現(xiàn)同一個ip有很多重復性的占用連接。
反查這些ip實例的日志,發(fā)現(xiàn)應用程序不能正常啟動,一直重啟,導致一直重復創(chuàng)建連接,又缺乏超時釋放的策略
先干掉不能啟動的幾個應用實例。
解決方案:一定要設置redis.conf的timeout 60,不能讓空閑連接占著茅坑不拉屎,把所有連接耗盡。容器一定要配置健康檢查,如果發(fā)現(xiàn)反復重啟的,要格外關注。
舉一反三:rocketmq、kafka、es呢
7 ?針對開發(fā)人員
?7.1 熟悉redis架構原理
別拿他當黑盒用,了解哪些操作會導致redis阻塞。否則就是給自己挖坑
7.2 使用redisson 而不是jedis
很傻瓜化的一個java客戶端,磨刀不誤砍柴工,多查看官網(wǎng)api文檔,如果你用的很辛苦,那可能是你的姿勢不對
?7.3 redis使用規(guī)范
在實際生產(chǎn)環(huán)境中,不可能給每個應用都建一套redis集群,一般是按業(yè)務領域分。
為了防止key沖突,各個應用直接約定key值加上約定的前綴來區(qū)分。
7.4 批量設置
在cluster模式下,對mset? mget命令限制很多,要求批量設置的key 都在同一臺redis實例上,否則報異常。
有什么替代方案呢?用hashmap