1.LRU
LRU(Least Recently Used,最近最久未使用算法)是一種常見的緩存淘汰算法,當緩存滿時,淘汰最近最久未使用的元素,在很多分布式緩存系統(如Redis, Memcached)中都有廣泛使用。其基本思想是如果一個數據在最近一段時間沒有被訪問到,那么可以認為在將來它被訪問的可能性也很小。因此,當緩存滿時,最久未被訪問的數據最先被淘汰。具體做法是將最近使用的元素存放到靠近緩存頂部的位置,當一個新條目被訪問時,LRU 將它放置到緩存的頂部。當緩存滿時,較早之前訪問的條目將從緩存底部被移除。
2.groupcache LRU Cache 簡介
在 Go 中,如果想使用 LRU 緩存,可以使用 Google Golang 團隊官方出品的開源庫 groupcache ,開源地址見 Github.groupcache。LRU 緩存通過 groupcache/lru/lru.go實現,它主要是封裝了一系列 LRU 緩存操作的相關的接口。主要有:
//創建一個 LRU Cache
func New(maxEntries int) *Cache //向 Cache 中插入一個 KV func (c *Cache) Add(key Key, value interface{}) //從 Cache 中獲取一個 key 對應的 value func (c *Cache) Get(key Key) (value interface{}, ok bool) //從 Cache 中刪除一個 key func (c *Cache) Remove(key Key) //從 Cache 中刪除最久未被訪問的數據 func (c *Cache) RemoveOldest() //獲取 Cache 中當前的元素個數 func (c *Cache) Len() //清空 Cache func (c *Cache) Clear()
注意,groupcache 中實現的 LRU Cache 並不是並發安全的,如果用於多個 Go 程並發的場景,需要加鎖。
當然,除了使用 groupcache 的 LRU Cache,其他開源的庫也可以參考一下,比如 HashiCorp 公司推出的 golang-lru。
3.源碼剖析
LRU Cache 基於 map 與 list,map 用於快速檢索,list 用於實現 LRU。具體實現如下:
package lru
import "container/list" //Cache 是一個 LRU Cache,注意它並不是並發安全的 type Cache struct { //MaxEntries 是 Cache 中實體的最大數量,0 表示沒有限制 MaxEntries int //OnEvicted 是一個可選的回調函數,當一個實體從 Cache 中被移除時執行 OnEvicted func(key Key, value interface{}) //ll是一個雙向鏈表指針,執行一個 container/list 包中的雙向鏈表 ll *list.List //cache 是一個 map,存放具體的 k/v 對,value 是雙向鏈表中的具體元素,也就是 *Element cache map[interface{}]*list.Element } //key 是接口,可以是任意類型 type Key interface{} //一個 entry 包含一個 key 和一個 value,都是任意類型 type entry struct { key Key value interface{} } //創建一個 LRU Cache。maxEntries 為 0 表示緩存沒有大小限制 func New(maxEntries int) *Cache { return &Cache{ MaxEntries: maxEntries, ll: list.New(), cache: make(map[interface{}]*list.Element), } } //向 Cache 中插入一個 KV func (c *Cache) Add(key Key, value interface{}) { if c.cache == nil { c.cache = make(map[interface{}]*list.Element) c.ll = list.New() } if ee, ok := c.cache[key]; ok { c.ll.MoveToFront(ee) ee.Value.(*entry).value = value return } ele := c.ll.PushFront(&entry{key, value}) c.cache[key] = ele if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { c.RemoveOldest() } } //傳入一個 key,返回一個是否有該 key 以及對應 value func (c *Cache) Get(key Key) (value interface{}, ok bool) { if c.cache == nil { return } if ele, hit := c.cache[key]; hit { c.ll.MoveToFront(ele) return ele.Value.(*entry).value, true } return } //從 Cache 中刪除一個 KV func (c *Cache) Remove(key Key) { if c.cache == nil { return } if ele, hit := c.cache[key]; hit { c.removeElement(ele) } } //從 Cache 中刪除最久未被訪問的數據 func (c *Cache) RemoveOldest() { if c.cache