緩存是提高程式效率最快最簡單的方案,計算機的各個部件都充斥着緩存,本系列文章主要討論內存緩存和常見的實現算法,另外因爲本喵覺得寫 golang 代碼很輕鬆故代碼將以 golang 書寫(但本喵會力圖簡單和清晰的註釋,不會使用golang應該也能看懂)。下面是系列文章內容概述:
- 基礎介紹 就是本文
- fifo 分析 實現
- lfu 分析 實現
- lru 分析 實現
- lru-k 分析 實現
此外本喵有爲 golang 開源一個緩存項目,上述算法都有實現,如果你只是想找到一個開源 golang 庫使用,可以考慮 https://github.com/powerpuffpenguin/gcache
緩存存儲的數據結構
通常緩存就是將要讀取的數據預先存儲到內存,通常由一個 key 和 value 組成,比如數據庫中有個用戶id爲1其名稱爲 anita,愛好是 music。每次從數據庫中取數據很慢,我們就可以爲她創建一條緩存,緩存的 key 就是 id,而名稱和愛好就是 value。
當擁有了這條緩存之後,下次查詢我們就可以先比對緩存中有沒有key爲1的數據,如果有這條數據就直接從緩存中獲取數據而不用查詢數據庫。
因爲在獲取緩存數據時傳入 key 返回 value 所以緩存存儲結構就需要考慮使用儘量能夠快速查詢的數據結構,這樣數組和鏈表就直接被淘汰了,因爲如果用這兩種結構就需要把 key 和 所有已緩存的key 對比才能返回對應的 value。通常使用的數據結構是 Map<Key,Value>。Map 可以使用二叉排序樹或者是 hashtable 主要看你使用的語言那種方便比如 c++ std::map 使用的是紅黑樹,golang 標準庫的 map 是 hashtable,但通常 hashtable 會比 二叉排序樹 提供更高的查詢效率。
緩存淘汰
緩存存儲的數據結構通常沒有差別,大部分都是直接使用的 Map,不同緩存算法的差別在於淘汰算法。
因爲緩存是爲了提升效率,所以緩存通常是存儲在比數據源所在介質更昂貴的存儲介質中,比如本系列文章討論的是將緩存存儲到內存(如果要存儲到其它地方可能需要考慮數據的序列化只會增加文章複雜度)。
因爲存儲介質昂貴所以容量有限,通常無法把數據源整個緩存下來,當緩存到達存儲上限但又有新數據需要緩存時,就需要淘汰一部分緩存來存新內容,如何儘量淘汰不被需要的數據而將熱點(經常需要查詢的數據)保存到緩存中是各算法要解決的問題和差異,並且也是緩存算法是否有效的衡量標準。
緩存接口
本文最後,我們定義一個 golang 緩存接口,後續將圍繞各個算法來實現這個接口:
type Cache interface {
Put(key, value interface{})
Get(key interface{}) (value interface{}, exists bool)
Delete(key interface{})
}
上述接口相當簡單,從其接口來看使用也相當簡單,基本和各語言標準庫提供的 Map 使用相差無幾。