FIFO算法
- 淘汰缓存中最早添加的记录,用一个双向队列实现,数据不断从队尾添加,当队列满时,移除队首元素
- 缺点:部分记录虽然是最早添加都,但后面可能也会被访问,但最早添加的数据不断被移除,也使得这部分数据不断被添`加、移除。降低性能。
//缓存接口
type Cache interface {
//添加数据。以key-value键值对的形式
Set(key string,value interface{})
//获取数据
Get(key string) interface{}
//删除数据
Del(key string)
//删除最老的数据
DelOldest()
//获取缓存队列长度
Len()int
}
type Value interface {
Len() int
}
//获取value数据的大小
func CalcLen(value interface{}) int {
var n int
switch v:=value.(type) {
case Value:
n=v.Len()
case string:
if runtime.GOARCH=="amd64"{
n=16+len(v)
}else {
n=8+len(v)
}
case bool,uint8,int8:
n=1
case int16,uint16:
n=2
case int32,uint32,float32:
n=4
case int64,uint64,float64:
n=8
case int, uint:
if runtime.GOARCH=="amd64"{
n=8
}else {
n=4
}
case complex64:
n=8
case complex128:
n=16
default:
panic(fmt.Sprintf("元素不合法"))
}
return n
}
//实现
type fifo struct {
//最大储存字节数
maxBytes int
//回调方法。可以在删除数据的时候被调用
onEvicted func(key string , value interface{})
//当前已使用的字节数
useBytes int
//缓存队列
ll *list.List
//缓存map集合。方便获取数据
cache map[string]*list.Element
}
//新增修改元素
func (f fifo) Set(key string, value interface{}) {
if e,ok := f.cache[key]; ok{
f.ll.MoveToBack(e)
en:=e.Value.(*entry)
f.useBytes=f.useBytes-c.CalcLen(en.value)+c.CalcLen(value)
en.value=value
}
en:=&entry{key: key,value: value}
e := f.ll.PushBack(en)
f.cache[key]=e
f.useBytes+=en.Len()
if f.useBytes>0&&f.useBytes>f.maxBytes{
f.DelOldest()
}
}
//获取缓存中的元素
func (f fifo) Get(key string) interface{} {
if e,ok := f.cache[key] ;ok{
return e.Value.(*entry).value
}
return nil
}
//删除元素
func (f fifo) Del(key string) {
if e,ok:=f.cache[key] ;ok{
f.removeElement(e)
}
}
//删除最老元素
func (f fifo) DelOldest() {
f.removeElement(f.ll.Front())
}
//获取队列长度
func (f fifo) Len() int {
return f.ll.Len()
}
func (f fifo) removeElement(e *list.Element) {
if e==nil{
return
}
f.ll.Remove(e)
ebn:=e.Value.(*entry)
f.useBytes-=ebn.Len()
delete(f.cache,ebn.key)
if f.onEvicted!=nil{
f.onEvicted(ebn.key,ebn.value)
}
}
type entry struct {
key string
value interface{}
}
func (e *entry)Len() int {
return c.CalcLen(e.value)
}
//生成缓存结构
func New(maxBytes int,onEvicted func(key string,value interface{})) c.Cache {
return &fifo{
maxBytes: maxBytes,
onEvicted: onEvicted,
ll: list.New(),
cache: make(map[string]*list.Element),
}
}
LFU算法
- 淘汰缓存中访问次数最少的数据,为缓存中的每一个数据添加一个count,每次访问count+1,缓存队列以count值排序,每次count加一队列重新排序,在删除数据时删除count最小的数据。
- 缺点:维护每个数据的访问次数对内存来说是一种浪费,并且如果数据的访问模式发送变化,则算法也需要时间重新排列,LFU算法受历史数据的影响比较大。
//淘汰缓存中访问次数最少的数据
type lfu struct {
maxBytes int
onEvicted func(key string,value interface{})
usedBytes int
queue *queue
cache map[string]*enerty
}
//添加数据,如果key已经存在,更新数据,count+1
func (l *lfu) Set(key string, value interface{}) {
if e,ok:=l.cache[key];ok{
l.usedBytes=l.usedBytes+caches.CalcLen(e.value)+caches.CalcLen(value)
l.queue.update(e,value,e.count+1)
return
}
//不存在则生成数据,添加到堆中
en:= &enerty{key: key,value: value}
heap.Push(l.queue,en)
l.cache[key] = en
l.usedBytes+=en.Len()
//如果内存不足就删除权重最小的点,就是最小堆的顶点
if l.maxBytes>0&&l.usedBytes>l.maxBytes{
l.removeElement(heap.Pop(l.queue))
}
}
func (l lfu) Get(key string) interface{} {
if e,ok:=l.cache[key];ok{
l.queue.update(e,e.value,e.count+1)
return e.value
}
return nil
}
//更新数据并重建堆
func (q *queue) update(e *enerty, value interface{}, i int) {
e.value = value
e.count=i
heap.Fix(q,e.index)
}
func (l lfu) Del(key string) {
if e,ok := l.cache[key];ok{
heap.Remove(l.queue,e.index)
l.removeElement(e)
}
}
func (l lfu) DelOldest() {
if l.queue.Len()==0{
return
}
l.removeElement(heap.Pop(l.queue))
}
func (l lfu) Len() int {
l.queue.Len()
}
func (l *lfu) removeElement(e interface{}) {
if e==nil{
return
}
en:=e.(*enerty)
delete(l.cache,en.key)
l.usedBytes-=en.Len()
if l.onEvicted!=nil{
l.onEvicted(en.key,en.value)
}
}
type enerty struct {
key string
value interface{}
count int
index int
}
type queue []*enerty
func (e *enerty)Len() int {
return caches.CalcLen(e.value)+4+4
}
func(q queue)Len()int{
return len(q)
}
func (q queue)Less(i,j int) bool {
return q[i].count<q[j].count
}
func (q *queue)Push( x interface{}) {
n:=len(*q)
en:=x.(*enerty)
en.index=n
*q=append(*q,en)
}
func (q queue)Swap(i,j int) {
q[i],q[j] = q[j],q[i]
q[i].index = i
q[j].index = j
}
func (q *queue)Pop() interface{} {
old := *q
n:=len(old)
en:= old[n-1]
old[n-1]=nil
en.index = -1
*q = old[0:n-1]
return en
}
func New(maxBytes int,onEvicted func(key string,value interface{})) caches.Cache {
q:=make(queue,0,1024)
return &lfu{
maxBytes: maxBytes,
onEvicted: onEvicted,
queue: &q,
cache: make(map[string]*enerty),
}
}