// Cache is an LRU cache. It is not safe for concurrent access. // groupcache的核心数据结构 type Cache struct { // MaxEntries is the maximum number of cache entries before // an item is evicted. Zero means no limit.
MaxEntries int// maxBytes 是允许使用的最大内存
// OnEvicted optionally specifies a callback function to be // executed when an entry is purged from the cache. OnEvicted func(key Key, value interface{})//提供一个淘汰值时的钩子函数
// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators type Key interface{} //只要是可以用来作为比较的对象,均可以作为groupcache的key
// 键值对 entry 是双向链表节点的数据类型,在链表中仍保存每个值对应的 key 的好处在于,淘汰队首节点时,需要用 key 从字典中删除对应的映射 type entry struct { key Key value interface{} }
// New creates a new Cache. // If maxEntries is zero, the cache has no limit and it's assumed // that eviction is done by the caller. // 方便实例化 Cache,实现 New() 函数 funcNew(maxEntries int) *Cache { return &Cache{ MaxEntries: maxEntries, ll: list.New(), cache: make(map[interface{}]*list.Element), } }
后续就是对这个数据结构常规的增删改查:
查找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Get looks up a key's value from the cache. // 第一步是从字典中找到对应的双向链表的节点,第二步,将该节点移动到队尾 // 如果键对应的链表节点存在,则将对应节点移动到队尾,并返回查找到的值。 // c.ll.MoveToFront(ele),即将链表中的节点 ele 移动到队尾(双向链表作为队列,队首队尾是相对的,在这里约定 front 为队尾) 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 }
// Add adds a value to the cache. //如果键存在,则更新对应节点的值,并将该节点移到队尾。 // 不存在则是新增场景,首先队尾添加新节点 &entry{key, value}, 并字典中添加 key 和节点的映射关系。 // 更新 c.nbytes,如果超过了设定的最大值 c.maxBytes,则移除最少访问的节点。 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
1 2 3 4 5 6 7 8 9
// Remove removes the provided key from the cache. func(c *Cache) Remove(key Key) { if c.cache == nil { return } if ele, hit := c.cache[key]; hit { c.removeElement(ele) } }
lru删除最近使用频率最低的key
1 2 3 4 5 6 7 8 9 10
// RemoveOldest removes the oldest item from the cache. func(c *Cache) RemoveOldest() { if c.cache == nil { return } ele := c.ll.Back() if ele != nil { c.removeElement(ele) } }