緩存之王 | Redis最佳實(shí)踐&開發(fā)規(guī)范&FAQ

Redis–從問題說起
(一)Run-to-Completion in a solo thread–Redis最大的問題

Ping命令判活:ping命令同樣受到慢查詢影響,如果引擎被卡住,則ping失敗;
Duplex Failure:sentinel由于慢查詢切備(備變主)再遇到慢查詢,Redis將出現(xiàn)OOS。
(二)擴(kuò)展為集群版,問題可解?

(三)Protocol問題–大量客戶端與引擎Fully-Meshed問題
擴(kuò)展性太差:基于Question-Answer模式,由于在Question/Answer里面沒有對(duì)應(yīng)的Sequence的存在,(如果不做復(fù)雜的轉(zhuǎn)換wrapper層)存儲(chǔ)引擎端沒法match請(qǐng)求和響應(yīng),只能采用Run-To-Completion來掛住鏈接;

C10K的問題:當(dāng)引擎掛住太多active鏈接的時(shí)候,性能下降太多。測(cè)試結(jié)果是當(dāng)有10k active連接時(shí),性能下降30-35%,由于引擎端掛住的鏈接不能被返回,用戶大量報(bào)錯(cuò)。

(四)“Could not get a resource from the pool”

Redis–不要觸碰邊界

高并發(fā)不等于高吞吐 大 Value 的問題:高速存儲(chǔ)并不會(huì)有特別大的高吞吐收益,相反會(huì)很危險(xiǎn);
數(shù)據(jù)傾斜和算力傾斜 bigKey 的問題:break掉存儲(chǔ)的分配律;熱點(diǎn)的問題,本質(zhì)上是cpu上的分配律不滿足;大 Range 的問題:對(duì)NoSQL的慢查詢和導(dǎo)致的危害沒有足夠的重視。
存儲(chǔ)邊界 Lua使用不當(dāng)造成的成本直線上升;數(shù)據(jù)傾斜帶來的成本飆升,無法有效利用;
對(duì)于 Latency 的理解問題(RT高) 存儲(chǔ)引擎的 Latency 都是P99 Latency,如:99.99%在1ms以內(nèi),99.5%在3ms以內(nèi),等;偶發(fā)性時(shí)延高是必然的。這個(gè)根因在于存儲(chǔ)引擎內(nèi)部的復(fù)雜性和熵。
Redis–阿里內(nèi)部開發(fā)規(guī)約
確定場(chǎng)景,是緩存(cache)還是存儲(chǔ)型;
Cache的使用原則是:“無它也可,有它更強(qiáng)”;
永遠(yuǎn)不要強(qiáng)依賴Cache,它會(huì)丟,也會(huì)被淘汰;
優(yōu)先設(shè)計(jì)合理的數(shù)據(jù)結(jié)構(gòu)和邏輯;
設(shè)計(jì)避免bigKey,就避免了80%的問題;
Keyspace能分開,就多申請(qǐng)幾個(gè)Redis實(shí)例;
pubsub不適合做消息分發(fā);
盡量避免用lua做事務(wù)。
我的服務(wù)對(duì)RT很敏感。>> 低RT能讓我的服務(wù)運(yùn)行的更好;
我把存儲(chǔ)都公用在一個(gè)redis里。>> 區(qū)分cache和內(nèi)存數(shù)據(jù)庫用法,區(qū)分應(yīng)用;
我有一個(gè)大排行榜/大集合/大鏈表/消息隊(duì)列;我覺得服務(wù)能力足夠了。>> 盡量拆散,服務(wù)能力不夠可通過分布式集群版可以打散;
我有一個(gè)特別大的Value,存在redis里,訪問能好些。>> redis吞吐量有瓶頸。
(一)BigKey–洪水猛獸

(二)Redis LUA JIT

對(duì)于JIT技術(shù)在存儲(chǔ)引擎中而言,“EVAL is evil”,盡量避免使用lua耗費(fèi)內(nèi)存和計(jì)算資源(省事不省心);
某些SDK(如Redisson)很多高級(jí)實(shí)現(xiàn)都內(nèi)置使用lua,開發(fā)者可能莫名走入CPU運(yùn)算風(fēng)暴中,須謹(jǐn)慎。
(三)Pubsub/Transaction/Pipeline

(四)KEYS 命令
Redis存儲(chǔ)key是無序的,匹配時(shí)必然全表掃描, key數(shù)目一多必然卡住,所以一定要去優(yōu)化。如下圖所例子中所示,修改為hash結(jié)構(gòu):
可以從全表掃描變?yōu)辄c(diǎn)查/部分range, 雖然hash結(jié)構(gòu)中field太多也會(huì)慢,但比keys性能提升一個(gè)到兩個(gè)數(shù)量級(jí)。

(五)除去KEYS,下面命令依然危險(xiǎn)
hgetall,smembers,lrange,zrange,exhgetall 直接與數(shù)據(jù)結(jié)構(gòu)的subkey(field)多少相關(guān),O(n),攜帶value爆網(wǎng)卡。建議使用scan來替代。
bitop,bitset 設(shè)置過遠(yuǎn)的bit會(huì)直接導(dǎo)致OOM。
flushall,flushdb 數(shù)據(jù)丟失。
配置中和ziplist相關(guān)的參數(shù) Redis在存儲(chǔ)相關(guān)數(shù)據(jù)結(jié)構(gòu)時(shí),數(shù)據(jù)量比較小,底層使用了ziplist結(jié)構(gòu),達(dá)到一定的量級(jí),比如key/field變多了,會(huì)轉(zhuǎn)換數(shù)據(jù)結(jié)構(gòu)。當(dāng)結(jié)構(gòu)在ziplist結(jié)構(gòu)體下時(shí),算力開銷變大,部分查詢變O(n)級(jí)別,匹配變O(m*n),極端情況容易打滿CPU,不過占用的內(nèi)存確實(shí)變少了(需要評(píng)估帶來的收益是否匹配付出的代價(jià)?)。
Cache場(chǎng)景,關(guān)閉AOF;內(nèi)存數(shù)據(jù)庫選擇雙副本
如果keyspace能夠分開,就申請(qǐng)不同的實(shí)例來隔離
set/hash/zset/list/tairhash/bloom/gis等大key(內(nèi)部叫做godkey)不要超過3000,會(huì)記錄sillylog
避免使用keys,hgetall,lrange0-1等大range(使用scan替代)
避免使用大value(10k以上就算大value,50k會(huì)記錄)
嚴(yán)禁設(shè)置低讀超時(shí)和緊密重試(建議設(shè)置200ms以下read timeout)
需要接受P99時(shí)延,對(duì)超時(shí)和慢做容錯(cuò)處理
盡量使用擴(kuò)展數(shù)據(jù)結(jié)構(gòu),避免使用lua
盡量避免pubsub和blocking的API
在阿里云上,如果機(jī)器宕機(jī),或者是機(jī)器后面有風(fēng)險(xiǎn),我們會(huì)做主動(dòng)運(yùn)維保證服務(wù)的穩(wěn)定性。
Redis–常見問題處理
(一)Tair/Redis內(nèi)存模型
鏈路內(nèi)存(動(dòng)態(tài)):主要包括Input buff、Output buff等,Input buff與Output buff跟每個(gè)客戶端的連接有關(guān)系,正常情況下比較小,但是當(dāng)Range操作的時(shí)候,或者有大key收發(fā)比較慢的時(shí)候,這兩個(gè)區(qū)的內(nèi)存會(huì)增大,影響數(shù)據(jù)區(qū),甚至?xí)斐蒓OM。還包括JIT Overhead、Fake Lua Link,包含了Code cache執(zhí)行緩存等等。
數(shù)據(jù)內(nèi)存:用戶數(shù)據(jù)區(qū),就是用戶實(shí)際存儲(chǔ)的value。
管理內(nèi)存(靜態(tài)):是靜態(tài)buff,啟動(dòng)的時(shí)候比較小,比較恒定。這個(gè)區(qū)域主要管理data的hash開銷,當(dāng)key非常多的時(shí)候,比如幾千萬、幾個(gè)億,會(huì)占用非常大的內(nèi)存。還包括Repl-buff、aof-buff(32M~64M)通常來說比較小。

(二)緩存分析–內(nèi)存分布統(tǒng)計(jì)、bigKey,key pattern
“實(shí)例管理”-->“CloudDBA”-->“緩存分析”-->“立即分析”;熱Key分析無需主動(dòng)觸發(fā)。
支持已有備份集;
支持自動(dòng)新建備份集。
社區(qū)版(2.8~6.0);
企業(yè)版(Tair)。
標(biāo)準(zhǔn)版;
讀寫分離版;
集群版。


(三)熱Key分析
使用入口:“實(shí)例管理” --> “CloudDBA” --> “緩存分析” -->“HotKey”;
使用須知:Tair版,或Redis版本>=redis4.0;
精確統(tǒng)計(jì)(非采樣),能抓出當(dāng)前所有 Per Key QPS > 3000的記錄;
方法1:緩存分析也可以分析出相對(duì)較熱的key,通過工具實(shí)現(xiàn);
方法2:最佳實(shí)踐,imonitor命令 + redis-faina 分析出熱點(diǎn)Key;

(四)Tair/Redis全鏈路診斷
ECS
Load,內(nèi)存等;
PPS限制。
客戶端
鏈接池滿;
RT高(跨地域,gc等);
建鏈接慢(K8s DNS等);
大查詢,發(fā)快收慢。
網(wǎng)絡(luò)
丟包,收斂;
運(yùn)營(yíng)商網(wǎng)絡(luò)抖動(dòng)。
VIP(SLB/NGLB)
建鏈接瓶頸(極少);
流量不均衡(少);
流量瓶頸(極少)。
Proxy
分發(fā)慢查;
流量高(擴(kuò)容proxy);
消息堆積;
Backend網(wǎng)絡(luò)抖動(dòng)。
DB
容量,CPU,流量(見前文);
主機(jī)故障,HA速度;
慢查詢。

(五)Tair/Redis診斷報(bào)告

(六)Tair/Redis慢日志
slowlog-log-slower-than:DB分片上慢日志閾值,不可設(shè)置過低!;
slowlog-max-len:DB分片slowlog鏈表最大保持長(zhǎng)度;
rt_threshold_ms:Proxy上慢日志閾值,不可設(shè)置過低!。

使用入口:“實(shí)例管理” --> “日志管理” --> “慢日志”;
使用須知:Tair版,或Redis版本>=redis4.0,具體查看幫助文檔;
可獲取近72小時(shí)內(nèi)的慢日志。
使用入口:“實(shí)例管理” --> “CloudDBA” --> “慢請(qǐng)求”;
實(shí)時(shí)獲取,能抓出當(dāng)前所有分片slowlog。



