极客兔兔7Days
GeeCache
- Day1
-
interface{}
:任意类型 -
缓存击穿
:一个高并发的请求查询一个缓存中不存在的数据项,因此这个请求穿透缓存直接到达后端数据库或数据源来获取数据。如果这种请求非常频繁,就会导致后端系统的负载突然增加,可能会使数据库或数据源响应变慢甚至宕机,从而影响整个系统的性能和稳定性。- 解决1:设置热点数据永不过期
- 解决2:使用锁机制确保只有一个请求去访问数据库,其他的请求等待这个请求的结果
- 解决3:设置时间更长的二级缓存
-
缓存淘汰策略
- FIFO:先进先出,也就是淘汰缓存中最老(最早添加)的记录
- LFU:最少使用,也就是淘汰缓存中访问频率最低的记录
- LRU:最近最少使用,相对于仅考虑时间因素的 FIFO 和仅考虑访问频率的 LFU,LRU 算法可以认为是相对平衡的一种淘汰算法。
-
list常用方法
:New()
、PushFront(v interface{}) *Element
、PushBack(v interface{}) *Element
、Remove(e *Element) interface{}
、Front() *Element
、Back() *Element
、Next() *Element
、Prev() *Element
-
使用list和map实现,cache中记录缓存最大容量和当前数据大小,对于刚访问的元素,将其移到list的最头部,表示最近刚使用过,删除时选择最尾部的数据进行删除,entry实际是list的节点数据类型,在删除对应节点后,同时删除map中的数据,实现查找、删除、增加、修改功能
-
代码
-
package gee import "container/list" type Cache struct { maxBytes int64 nbytes int64 ll *list.List cache map[string]*list.Element } type entry struct { key string value Value } type Value interface { Len() int } func New(maxBytes int64) *Cache { return &Cache{ maxBytes: maxBytes, ll: list.New(), nbytes: 0, cache: make(map[string]*list.Element), } } // 查找 func (c *Cache) Get(key string) (value Value, ok bool) { if ele, ok := c.cache[key]; ok { // 假设头部是队尾 c.ll.MoveToFront(ele) kv := ele.Value.(*entry) return kv.value, true } return nil, false } // 删除 func (c *Cache) Delete() { ele := c.ll.Back() if ele != nil { c.ll.Remove(ele) // 类型断言 kv := ele.Value.(*entry) delete(c.cache, kv.key) c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len()) } } // 添加 func (c *Cache) Add(key string, value Value) { if ele, ok := c.cache[key]; ok { c.ll.MoveToFront(ele) kv := ele.Value.(*entry) c.nbytes += int64(value.Len()) - int64(kv.value.Len()) kv.value = value } else { ele := c.ll.PushFront(&entry{key, value}) c.nbytes += int64(len(key)) + int64(value.Len()) c.cache[key] = ele } for c.maxBytes != 0 && c.maxBytes < c.nbytes { c.Delete() } } func (c *Cache) Len() int { return c.ll.Len() }
-