作者:杜偉
在碼農(nóng)的世界里箕肃,一直以來(lái)都有一個(gè)信仰:只要應(yīng)用使用了緩存茉帅,性能就會(huì)翻倍;用上緩存的應(yīng)用就像是打通任督二脈的武林高手涨冀,內(nèi)力生生不息。但是今天我想跟各位猿類朋友聊一聊自己在使用緩存時(shí)遇到的那些坑(這里主要講對(duì)象緩存應(yīng)用部分麦萤,想了解全面的推薦閱讀《架構(gòu)真經(jīng)》)鹿鳖。
案例一
前幾天突然發(fā)現(xiàn) Redis 監(jiān)控顯示某 Redis 實(shí)例內(nèi)存使用量突破了 14G 大關(guān)扁眯,當(dāng)時(shí)我就震驚了,這是要翻車的節(jié)奏啊翅帜。通過(guò) info 指令查看姻檀,發(fā)現(xiàn) key 的量并不是太高,所以懷疑有個(gè)別業(yè)務(wù) value 的 size 比較大涝滴,為了保證生產(chǎn)不受影響绣版,從生產(chǎn)上導(dǎo)出 rdb 文件進(jìn)行分析。通過(guò)
rdb -c memory redis_dump.rdb —bytes 1024 -f memory.csv
將大于1024字節(jié)的 key 導(dǎo)出歼疮。然后進(jìn)行排序:
sort -t, -k4nr memory.csv |head -n 20杂抽,
終于找到了罪魁禍?zhǔn)住H缦聢D:
緊急刪除該 key 臨時(shí)解決了問(wèn)題韩脏,經(jīng)復(fù)盤發(fā)現(xiàn)缩麸,此問(wèn)題是開發(fā)時(shí)緩存使用不當(dāng),所有信息都不過(guò)期赡矢,數(shù)據(jù)只能越增越大杭朱。
總結(jié):緩存中放置的應(yīng)該是熱數(shù)據(jù),同時(shí)緩存策略也存在問(wèn)題济竹,應(yīng)該針對(duì)每條記錄設(shè)置不同的 key ,而不是都放在一個(gè) key 下霎槐。
案例二
某歷史項(xiàng)目最近總是報(bào)警(系統(tǒng)負(fù)載升高送浊,響應(yīng)變慢,應(yīng)用頻繁 GC )丘跌,只能頻繁重啟應(yīng)用袭景。分析 Dump 文件,發(fā)現(xiàn)創(chuàng)建的對(duì)象非常多闭树,但沒有明顯的占用大戶耸棒。于是繼續(xù)分析占用內(nèi)存較大的對(duì)象,發(fā)現(xiàn)都是 Hibernate 的代理對(duì)象报辱;接下來(lái)去看 Hibernate 的配置与殃,發(fā)現(xiàn)開啟了二級(jí)緩存,同時(shí) Ehcache 中沒有控制緩存對(duì)象的個(gè)數(shù)碍现,導(dǎo)致應(yīng)用啟動(dòng)一段時(shí)間后幅疼,隨著緩存對(duì)象的增多,很快會(huì)導(dǎo)致 GC 了昼接。于是我們緊急上線爽篷,關(guān)閉了部分對(duì)象的緩存,問(wèn)題得到了初步緩解慢睡。
總結(jié):當(dāng)使用本地緩存(如 Ehcache )時(shí)逐工,一定要嚴(yán)格控制緩存對(duì)象的個(gè)數(shù)及生命周期铡溪。由于 JVM 的特性,過(guò)多的緩存對(duì)象會(huì)極大的影響 JVM 性能泪喊。
案例三
某個(gè)正常運(yùn)行的應(yīng)用突然報(bào)警線程數(shù)高棕硫,之后很快就內(nèi)存溢出。查看日志發(fā)現(xiàn)無(wú)法連接 Memcached窘俺,繼而查看 Memcached 配置饲帅,發(fā)現(xiàn)連接數(shù)過(guò)高,拒絕連接瘤泪。而應(yīng)用連接 Memcached 的超時(shí)時(shí)間較長(zhǎng)灶泵,導(dǎo)致請(qǐng)求線程不斷堆積,最后應(yīng)用內(nèi)存溢出对途。臨時(shí)解決方案是先增加 Memcached 的連接數(shù)赦邻,之后應(yīng)用修改連接超時(shí)時(shí)間。
總結(jié):當(dāng)我們?cè)谑褂眠h(yuǎn)程緩存(如 Redis实檀、Memcached )時(shí)惶洲,一定要對(duì)超時(shí)時(shí)間進(jìn)行控制,一般來(lái)講緩存的總體響應(yīng)時(shí)間不能高于 50ms 膳犹,否則緩存會(huì)成為應(yīng)用的負(fù)擔(dān)恬吕,而不是幫手。
案例四
某項(xiàng)目關(guān)鍵業(yè)務(wù)忽然報(bào)警業(yè)務(wù)波動(dòng)须床,查看應(yīng)用日志發(fā)現(xiàn)訪問(wèn) Redis 異常铐料,查看 Redis 發(fā)現(xiàn)做了主備切換;這次超時(shí)時(shí)間設(shè)置沒問(wèn)題豺旬,但沒有針對(duì)緩存不可用做降級(jí)處理钠惩,導(dǎo)致業(yè)務(wù)流程中斷。
總結(jié):使用緩存時(shí)族阅,一定要有降級(jí)處理篓跛;尤其是關(guān)鍵業(yè)務(wù)環(huán)節(jié)。
案例五
某項(xiàng)目使用緩存后坦刀,開發(fā)測(cè)試沒問(wèn)題愧沟,上生產(chǎn)后,服務(wù)卻不可用鲤遥。查看日志發(fā)現(xiàn)是該應(yīng)用的緩存 key 與其他應(yīng)用緩存的 key 沖突央渣,導(dǎo)致錯(cuò)誤。
總結(jié):緩存使用時(shí)渴频,一定要有隔離的設(shè)計(jì)芽丹。比如 Redis 可以選擇不同的 DB,或者 Ehcache 定義不同的 Cache 名卜朗。如果這些都不方便實(shí)施拔第,至少也要有 key 的命名規(guī)范咕村,否則天知道會(huì)發(fā)生什么。
案例六
某項(xiàng)目使用 Redis 緩存臨時(shí)數(shù)據(jù)蚊俺,上線后出現(xiàn)錯(cuò)誤數(shù)據(jù)懈涛,但由于沒有開發(fā)管理功能,導(dǎo)致發(fā)生問(wèn)題時(shí)泳猬,看不到數(shù)據(jù)批钠,也無(wú)法管理,使得應(yīng)急時(shí)間較長(zhǎng)得封。
總結(jié):當(dāng)使用緩存后埋心,一定提供相應(yīng)的管理手段。否則發(fā)生事故時(shí)忙上,只能兩眼一抹黑拷呆。
案例七
某應(yīng)用在訪問(wèn)時(shí)經(jīng)常時(shí)快時(shí)慢,同時(shí) DB 負(fù)載也忽高忽低疫粥;分析代碼發(fā)現(xiàn)項(xiàng)目中緩存設(shè)置了一個(gè)固定的失效時(shí)間茬斧,當(dāng)緩存失效時(shí),會(huì)造成一段時(shí)間內(nèi)訪問(wèn) DB 的請(qǐng)求非常集中梗逮。
總結(jié):簡(jiǎn)單方案就是將緩存失效時(shí)間分散開项秉,比如我們可以將失效時(shí)間設(shè)置為一個(gè)區(qū)間內(nèi)的隨機(jī)值,這樣每一個(gè)緩存的過(guò)期時(shí)間的重復(fù)率就會(huì)降低慷彤,就很難引發(fā)集體失效的事件娄蔼。
案例八
某計(jì)費(fèi)項(xiàng)目發(fā)現(xiàn)計(jì)算結(jié)果有誤差,分析日志時(shí)發(fā)現(xiàn):系統(tǒng)修改規(guī)則后瞬欧,較長(zhǎng)時(shí)間仍在使用舊規(guī)則贷屎。查看 Ehcache 配置文件發(fā)現(xiàn)過(guò)期時(shí)間很長(zhǎng)罢防,導(dǎo)致規(guī)則更新后艘虎,很長(zhǎng)時(shí)間不生效,部分訂單計(jì)算費(fèi)用出現(xiàn)誤差咒吐。
總結(jié):使用本地緩存又需要數(shù)據(jù)一致性時(shí)野建,可以考慮用 Zookeeper 之類的協(xié)調(diào)服務(wù),實(shí)現(xiàn)一個(gè)更高效的緩存更新機(jī)制恬叹。
案例九
這是一個(gè)緩存穿透的案例候生,某模塊設(shè)計(jì)使用了緩存,但發(fā)現(xiàn)數(shù)據(jù)庫(kù)負(fù)載并沒有大幅下降绽昼。分析代碼發(fā)現(xiàn)唯鸭,緩存邏輯如下:數(shù)據(jù)庫(kù)存在紀(jì)錄則放到緩存中,不存在則下次仍然會(huì)訪問(wèn)數(shù)據(jù)庫(kù)硅确;導(dǎo)致不存在的訂單頻繁查詢時(shí)目溉,緩存沒有效果明肮。
總結(jié):緩存穿透是一個(gè)常見問(wèn)題,我們需要把 null 也作為一種緩存結(jié)果缭付,否則此類情況等于緩存是失效的柿估。
結(jié)尾
以上這些案例都是我們親身經(jīng)歷的血的教訓(xùn),在研發(fā)過(guò)程也是很容易忽略的點(diǎn)陷猫。誠(chéng)然使用緩存是提高應(yīng)用性能的有效手段秫舌,但沒有深入的分析與設(shè)計(jì)就很容易造成災(zāi)難性的影響。我思故我在绣檬,我們?cè)愁愖畲蟮膬?yōu)點(diǎn)就是思考能力足陨,希望這些小案例能引起大家更深層次的思考。另外河咽,軟件設(shè)計(jì)思想來(lái)源于生活钠右,比如看看 CPU 的設(shè)計(jì),有一級(jí)緩存忘蟹、二級(jí)緩存飒房,估計(jì)有很多小伙伴也在做緩存設(shè)計(jì)時(shí)這樣做了。生活中處處有設(shè)計(jì)媚值,只要用心總能觀察到狠毯。