題混成一件事)
緩存穿透、擊穿和雪崩別把三種問(wèn)題混成一件事緩存能夠減少數(shù)據(jù)庫(kù)讀取也能讓高頻頁(yè)面更快返回。但緩存不是簡(jiǎn)單加在數(shù)據(jù)庫(kù)前面就結(jié)束了。請(qǐng)求查不到數(shù)據(jù)、熱點(diǎn)剛好失效、大量鍵同時(shí)過(guò)期、緩存服務(wù)本身異常都會(huì)讓流量重新落到后端。若團(tuán)隊(duì)把這些情況統(tǒng)稱(chēng)為“緩存崩了”往往會(huì)套錯(cuò)方案給不存在的數(shù)據(jù)加鎖給熱點(diǎn)一味延長(zhǎng)過(guò)期時(shí)間或者只靠增加 Redis 容量來(lái)解決批量失效。更穩(wěn)妥的做法是先區(qū)分現(xiàn)象再根據(jù)數(shù)據(jù)是否存在、熱點(diǎn)程度、過(guò)期分布和后端承受能力設(shè)計(jì)防護(hù)。緩存策略應(yīng)服務(wù)于業(yè)務(wù)正確性和可用性不能為了提高命中率而返回不該返回的數(shù)據(jù)或把過(guò)期數(shù)據(jù)無(wú)限期留在系統(tǒng)里。穿透先判斷請(qǐng)求是否有意義緩存穿透通常指請(qǐng)求查詢(xún)的對(duì)象在緩存和數(shù)據(jù)源中都不存在。可能是用戶(hù)輸入錯(cuò)誤、爬蟲(chóng)探測(cè)、接口被濫用也可能是數(shù)據(jù)剛刪除后仍有舊鏈接被訪問(wèn)。每次都直接查數(shù)據(jù)庫(kù)即使單次查詢(xún)很輕也會(huì)在高并發(fā)下形成持續(xù)壓力。處理前應(yīng)確認(rèn)“不存在”在業(yè)務(wù)上是什么意思。有些資源確實(shí)永遠(yuǎn)不可能存在可以較早拒絕有些對(duì)象則可能剛創(chuàng)建、尚未同步過(guò)早判斷不存在會(huì)影響正常用戶(hù)。輸入?yún)?shù)校驗(yàn)、權(quán)限校驗(yàn)和業(yè)務(wù)狀態(tài)檢查應(yīng)放在合適位置不能把所有未知標(biāo)識(shí)一律當(dāng)作攻擊。對(duì)可確認(rèn)的無(wú)效請(qǐng)求可以使用短期的空結(jié)果緩存減少重復(fù)訪問(wèn)下沉。過(guò)期時(shí)間需要根據(jù)數(shù)據(jù)創(chuàng)建和刪除的時(shí)效設(shè)計(jì)不能無(wú)限保存。布隆過(guò)濾器等概率結(jié)構(gòu)也可以用于較大規(guī)模的已知集合它能可靠地排除某些不在集合中的鍵但“可能存在”并不代表數(shù)據(jù)庫(kù)一定有記錄后續(xù)仍要正常查詢(xún)和處理。擊穿關(guān)注的是單個(gè)熱點(diǎn)重建緩存擊穿與穿透不同它針對(duì)的是本來(lái)存在且訪問(wèn)量很高的對(duì)象。當(dāng)一個(gè)熱點(diǎn)鍵過(guò)期或失效時(shí)很多請(qǐng)求同時(shí)發(fā)現(xiàn)未命中并發(fā)回源加載同一份數(shù)據(jù)。后端壓力集中在一個(gè)對(duì)象的重建上常在活動(dòng)頁(yè)、熱門(mén)內(nèi)容或高頻配置讀取時(shí)出現(xiàn)。第一步是識(shí)別真正熱點(diǎn)而不是給所有鍵加復(fù)雜鎖??梢酝ㄟ^(guò)訪問(wèn)量、重建成本和業(yè)務(wù)重要性判斷哪些對(duì)象需要特殊處理。普通鍵偶爾并發(fā)回源可能并不值得引入額外協(xié)調(diào)高熱點(diǎn)且重建昂貴的鍵才需要明確誰(shuí)負(fù)責(zé)刷新、其他請(qǐng)求如何等待或降級(jí)。一種策略是在緩存失效后只允許有限請(qǐng)求觸發(fā)重建其他請(qǐng)求短暫等待、返回受控的舊值或給出可理解的稍后重試。鎖或單飛機(jī)制只能協(xié)調(diào)重建過(guò)程仍需要設(shè)置超時(shí)、失敗處理和持有者身份避免重建任務(wù)本身卡住時(shí)把所有請(qǐng)求長(zhǎng)期阻塞。邏輯過(guò)期是另一種思路物理緩存保留數(shù)據(jù)業(yè)務(wù)根據(jù)記錄的更新時(shí)間決定它是否需要刷新。一個(gè)后臺(tái)任務(wù)更新數(shù)據(jù)其余請(qǐng)求在有限窗口內(nèi)可以讀取舊值。這適合能夠接受短暫陳舊結(jié)果的場(chǎng)景例如內(nèi)容展示或非實(shí)時(shí)配置對(duì)余額、庫(kù)存和強(qiáng)實(shí)時(shí)狀態(tài)則不應(yīng)為了平滑而返回過(guò)期結(jié)果。雪崩看的是一批緩存同時(shí)失去作用緩存雪崩涉及的不是單個(gè)熱點(diǎn)而是一大批鍵在相近時(shí)間失效或緩存服務(wù)整體不可用。批量初始化時(shí)統(tǒng)一設(shè)置過(guò)期時(shí)間、定時(shí)任務(wù)集中刷新、發(fā)布時(shí)誤刪大范圍鍵都可能造成前者單點(diǎn)部署、網(wǎng)絡(luò)故障和容量耗盡則可能觸發(fā)后者。過(guò)期時(shí)間適度分散能夠降低同時(shí)回源的概率但它只是風(fēng)險(xiǎn)緩解不是絕對(duì)保證。隨機(jī)范圍要結(jié)合業(yè)務(wù)過(guò)期要求設(shè)計(jì)不能讓需要準(zhǔn)時(shí)更新的內(nèi)容被隨意拖延。更重要的是避免把大量關(guān)鍵鍵綁定到同一個(gè)刷新時(shí)刻并對(duì)批量操作設(shè)置范圍檢查和回滾路徑。緩存服務(wù)故障時(shí)后端需要有明確的保護(hù)策略。數(shù)據(jù)庫(kù)連接池、并發(fā)預(yù)算、限流和降級(jí)可以防止所有請(qǐng)求一起沖向數(shù)據(jù)源有些非關(guān)鍵功能可以暫時(shí)關(guān)閉或使用有限的本地結(jié)果。策略應(yīng)區(qū)分接口優(yōu)先級(jí)不能讓一項(xiàng)低價(jià)值查詢(xún)擠占核心交易所需資源。多副本、合理的客戶(hù)端超時(shí)和故障切換有助于提高緩存服務(wù)可用性但也要測(cè)試切換期間的行為??蛻?hù)端無(wú)限重試或同步等待可能在服務(wù)恢復(fù)前先把應(yīng)用線(xiàn)程耗盡。緩存數(shù)據(jù)的正確性要有邊界緩存中保存什么、何時(shí)更新、刪除后怎樣失效都必須與數(shù)據(jù)寫(xiě)入路徑協(xié)調(diào)。僅在讀取端添加緩存不處理寫(xiě)入后的更新或失效會(huì)讓用戶(hù)長(zhǎng)期看到舊數(shù)據(jù)。寫(xiě)后刪除、更新緩存、事件驅(qū)動(dòng)刷新等方式各有適用條件需要結(jié)合并發(fā)寫(xiě)入和失敗恢復(fù)設(shè)計(jì)。不要把緩存當(dāng)作權(quán)限判斷的最終來(lái)源。個(gè)性化數(shù)據(jù)、賬戶(hù)狀態(tài)和敏感信息需要按用戶(hù)與權(quán)限隔離緩存鍵或內(nèi)容范圍不清楚時(shí)可能把一個(gè)用戶(hù)的結(jié)果返回給另一個(gè)用戶(hù)。命中率再高也無(wú)法彌補(bǔ)這種錯(cuò)誤。觀測(cè)指標(biāo)也應(yīng)關(guān)注正確性命中與未命中、回源時(shí)間、重建失敗、空結(jié)果比例、過(guò)期數(shù)據(jù)讀取和緩存服務(wù)錯(cuò)誤都能幫助判斷策略是否在按預(yù)期工作。只有 QPS 和內(nèi)存使用量不足以說(shuō)明緩存真的保護(hù)了后端。用真實(shí)失敗場(chǎng)景驗(yàn)證策略上線(xiàn)前可以在受控環(huán)境模擬幾種關(guān)鍵情況持續(xù)請(qǐng)求不存在對(duì)象、熱點(diǎn)鍵同時(shí)過(guò)期、刷新任務(wù)失敗、大批鍵需要重新加載、緩存節(jié)點(diǎn)短暫不可用。觀察數(shù)據(jù)庫(kù)是否仍在可承受范圍內(nèi)用戶(hù)是否得到適當(dāng)反饋數(shù)據(jù)是否會(huì)錯(cuò)誤地長(zhǎng)期陳舊。發(fā)生異常后團(tuán)隊(duì)?wèi)?yīng)知道先保護(hù)什么、誰(shuí)有權(quán)關(guān)閉某項(xiàng)緩存策略、如何恢復(fù)和核對(duì)數(shù)據(jù)。運(yùn)行手冊(cè)不必寫(xiě)得很長(zhǎng)但應(yīng)避免現(xiàn)場(chǎng)臨時(shí)執(zhí)行大范圍刪除或無(wú)界重試。緩存穿透、擊穿和雪崩的共同點(diǎn)是都會(huì)增加回源壓力原因和處理方式卻不同。先識(shí)別對(duì)象是否存在、是否是熱點(diǎn)、是否會(huì)批量失效再選擇短期空值、受控重建、過(guò)期分散和后端保護(hù)緩存才能在壓力到來(lái)時(shí)真正發(fā)揮作用。