|
|
package lfu
|
|
|
|
|
|
import (
|
|
|
"cache"
|
|
|
"container/heap"
|
|
|
)
|
|
|
|
|
|
// lfu
|
|
|
// @Description:LFU,即最少使用,也就是淘汰缓存中访问频率最低的记录。
|
|
|
// LFU 认为,如果数据过去被访问多次,那么将来被访问的频率也更高。
|
|
|
// LFU 的实现需要维护一个按照访问次数排序的队列,每次访问,访问次数加 1,队列重新排序,淘汰时选择访问次数最少的即可
|
|
|
// 利用最小堆属性对访问次数排序实现
|
|
|
type lfu struct {
|
|
|
// 缓存最大容量
|
|
|
maxBytes int
|
|
|
// 当一个 entry 从缓存中移除时调用该回调函数,默认为 nil
|
|
|
onEvicted func(key string, value any)
|
|
|
//已使用的字节数,只包括值,key 不算
|
|
|
usedBytes int
|
|
|
queue *queue
|
|
|
cache map[string]*entry
|
|
|
}
|
|
|
|
|
|
func New(maxBytes int, onEvicted func(key string, value any)) cache.Cache {
|
|
|
q := make(queue, 0, 1024)
|
|
|
return &lfu{
|
|
|
maxBytes: maxBytes,
|
|
|
onEvicted: onEvicted,
|
|
|
queue: &q,
|
|
|
cache: make(map[string]*entry),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (l *lfu) Set(key string, value any) {
|
|
|
if e, ok := l.cache[key]; ok {
|
|
|
l.usedBytes = l.usedBytes - cache.CalcLen(e.value) + cache.CalcLen(value)
|
|
|
l.queue.update(e, value, e.weight+1)
|
|
|
} else {
|
|
|
en := &entry{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) any {
|
|
|
if e, ok := l.cache[Key]; ok {
|
|
|
l.queue.update(e, e.value, e.weight+1)
|
|
|
return e.value
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (l *lfu) Del(Key string) {
|
|
|
if e, ok := l.cache[Key]; ok {
|
|
|
heap.Remove(l.queue, e.index)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (l *lfu) DelOldest() {
|
|
|
if l.queue.Len() == 0 {
|
|
|
return
|
|
|
}
|
|
|
l.removeElement(heap.Pop(l.queue))
|
|
|
}
|
|
|
|
|
|
func (l *lfu) Len() int {
|
|
|
return l.queue.Len()
|
|
|
}
|
|
|
|
|
|
func (l *lfu) removeElement(x any) {
|
|
|
if x == nil {
|
|
|
return
|
|
|
}
|
|
|
en := x.(*entry)
|
|
|
delete(l.cache, en.key)
|
|
|
l.usedBytes -= en.Len()
|
|
|
if l.onEvicted != nil {
|
|
|
l.onEvicted(en.key, en.value)
|
|
|
}
|
|
|
}
|