背景
在請(qǐng)求具有一定的重復(fù)性的業(yè)務(wù)場(chǎng)景下,客戶(hù)端或者服務(wù)端將請(qǐng)求結(jié)果保存在緩存中,可以大大減少服務(wù)端壓力和延時(shí)(尤其是請(qǐng)求方采用負(fù)載均衡策略,比如IP一致性哈希時(shí))。通常緩存的大小是固定的,在大部分情況下緩存應(yīng)該盡量保存熱度較高的請(qǐng)求結(jié)果以提高緩存命中率,有多種緩存淘汰策略如:LRU、定期淘汰等,在服務(wù)運(yùn)行過(guò)程中緩存淘汰操作會(huì)在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行
服務(wù)在穩(wěn)定運(yùn)行時(shí),其緩存命中率也處于穩(wěn)定狀態(tài)。不合理的緩存淘汰機(jī)制或者由于其他原因?qū)е戮彺婷新蚀蠓陆担瑢?huì)對(duì)服務(wù)性能產(chǎn)生很大挑戰(zhàn),甚至導(dǎo)致請(qǐng)求方熔斷;另外,如果服務(wù)啟動(dòng)時(shí)緩存并沒(méi)有“熱”起來(lái)甚至已經(jīng)被清空,那么冷啟動(dòng)時(shí)其延時(shí)會(huì)比穩(wěn)定運(yùn)行時(shí)更高,解決這個(gè)問(wèn)題就需要在服務(wù)啟動(dòng)前對(duì)緩存進(jìn)行預(yù)熱,即warmup操作
高并發(fā)服務(wù)通常會(huì)采用本地+遠(yuǎn)程雙層緩存的架構(gòu),遠(yuǎn)程緩存可以由 codis 集群或者其他 KV 集群實(shí)現(xiàn),本地緩存位于服務(wù)所在機(jī)器的內(nèi)存中
基本功能
-
并發(fā)讀寫(xiě)
- 緩存需要對(duì)多個(gè)讀寫(xiě)操作提供并發(fā)支持,并且最大限度的減少各操作的耗時(shí)(使用時(shí)也需要注意數(shù)據(jù)類(lèi)型對(duì)耗時(shí)的影響
- 比如使用int64_t代替std::string類(lèi)型作為緩存的key會(huì)降低查詢(xún)耗時(shí))
- 分桶存儲(chǔ),鎖粒度的控制
- 緩存需要對(duì)多個(gè)讀寫(xiě)操作提供并發(fā)支持,并且最大限度的減少各操作的耗時(shí)(使用時(shí)也需要注意數(shù)據(jù)類(lèi)型對(duì)耗時(shí)的影響
-
強(qiáng)制失效
- 插入、讀取是緩存的常規(guī)操作,另外也有業(yè)務(wù)場(chǎng)景需要提供強(qiáng)制讓某條緩存失效的功能,同樣需要并發(fā)安全
-
淘汰
- LRU、定期過(guò)期等策略
-
預(yù)熱
- 本地緩存預(yù)熱會(huì)增加單個(gè)服務(wù)啟動(dòng)時(shí)間
- 遠(yuǎn)程緩存預(yù)熱決定整體服務(wù)可上線(xiàn)時(shí)間
- 如果是重構(gòu)的新服務(wù),逐漸增加服務(wù)的流量(另一方面是穩(wěn)定性的考慮)來(lái)預(yù)熱緩存
- 通常遠(yuǎn)程緩存需要高可用
本地緩存
實(shí)現(xiàn)
C++ 實(shí)現(xiàn)的本地緩存可參考 facebook 的:LRU緩存、分桶 LRU 緩存,以及基于上述緩存實(shí)現(xiàn)的定期淘汰緩存
預(yù)熱方案
本地緩存位于內(nèi)存中,服務(wù)停止后會(huì)其占用內(nèi)存也會(huì)失效。預(yù)熱通常發(fā)生在(時(shí)間較短的)服務(wù)上線(xiàn)過(guò)程,在可認(rèn)為上線(xiàn)前的緩存是”熱“的場(chǎng)景下,對(duì)本地緩存進(jìn)行預(yù)熱有以下兩種思路。預(yù)熱緩存除了會(huì)增加服務(wù)停止、啟動(dòng)時(shí)長(zhǎng),也會(huì)增加服務(wù)上下線(xiàn)等部署相關(guān)操作的復(fù)雜度,例如使用 K8s 部署有狀態(tài)服務(wù)(狀態(tài)與宿主機(jī) IP 相關(guān))時(shí),服務(wù)不一定被部署到上次部署的宿主機(jī)上,這時(shí)則可以考慮通過(guò)其他方法如負(fù)載均衡控制 QPS 以達(dá)到預(yù)熱緩存的目的
回放請(qǐng)求
如果只是由于服務(wù)啟動(dòng)后、接收請(qǐng)求前,某些模塊未初始化導(dǎo)致的耗時(shí)較高,只需要在服務(wù)啟動(dòng)后發(fā)起下請(qǐng)求以完成懶初始化
如果是由于緩存為空導(dǎo)致耗時(shí)較高,可以在服務(wù)啟動(dòng)后解析最近請(qǐng)求日志并回放;另一種方法是在服務(wù)停止前將本地緩存中的key全部dump出來(lái),服務(wù)啟動(dòng)后讀取這些key并構(gòu)造請(qǐng)求,計(jì)算對(duì)應(yīng)的value寫(xiě)入緩存
回放請(qǐng)求的方法比較簡(jiǎn)單,但是當(dāng)服務(wù)在本地緩存命中率較低時(shí)處理warmup請(qǐng)求耗時(shí)也較高,進(jìn)而增加了服務(wù)啟動(dòng)的時(shí)間,降低上線(xiàn)效率。有時(shí)受限于上線(xiàn)時(shí)長(zhǎng),無(wú)法達(dá)到預(yù)熱目標(biāo)數(shù)量
dump & load
如果服務(wù)在本地緩存命中率較低時(shí)處理warmup請(qǐng)求耗時(shí)較高,可以考慮在服務(wù)停止前將本地緩存整個(gè)dump到磁盤(pán)或者分布式存儲(chǔ)系統(tǒng)等等,服務(wù)啟動(dòng)后將dump內(nèi)容再load到緩存
local-cache-warmup項(xiàng)目中實(shí)現(xiàn)了對(duì)上述 定期淘汰緩存 執(zhí)行預(yù)熱的工具,用戶(hù)需要自己實(shí)現(xiàn)對(duì)本地緩存內(nèi)容的序列化與反序列化操作(本地緩存經(jīng)常會(huì)保存一些復(fù)雜的資源型數(shù)據(jù)結(jié)構(gòu)),工具內(nèi)部將并發(fā)執(zhí)行dump和load操作,并且爭(zhēng)取最大程度降低其耗時(shí)。針對(duì) 定期淘汰緩存,如果其load操作比正常請(qǐng)求結(jié)果寫(xiě)入緩存快很多的話(huà),還需要避免緩存集中在較短時(shí)間內(nèi)失效的情況(這可能對(duì)服務(wù)性能影響較大),工具中也提供了warmup接口額外指定一個(gè)浮動(dòng)秒數(shù) ,代表本次寫(xiě)入的緩存將在策略期限以?xún)?nèi)或以外浮動(dòng)秒數(shù)的區(qū)間失效
負(fù)載均衡
通過(guò)適當(dāng)?shù)?a href="http://www.rzrgm.cn/wangzhiyi/p/13765762.html" target="_blank">負(fù)載均衡策略,減少服務(wù)冷啟動(dòng)時(shí)接收到的 QPS
分布式緩存
待補(bǔ)充
參考
How to write a large buffer into a binary file in C++, fast?
mmap, memcpy to copy file from A to B
浙公網(wǎng)安備 33010602011771號(hào)