极客兔兔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中的数据,实现查找、删除、增加、修改功能
-
代码
-
go">package geeimport "container/list"type Cache struct {maxBytes int64nbytes int64ll *list.Listcache map[string]*list.Element }type entry struct {key stringvalue 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() }
-