LFU淘汰策略-go简单实现


/*
@Time : 2022/2/25 12:13 下午
@Author : yuyunqing
@File : lfu_lru
@Software: GoLand
*/
package lfu_lru

import (
   "log"
   "time"
)

// LRU缓存机制(Least Recently Used)(看时间)
//
//在缓存满的时候,删除缓存里最久未使用的数据,然后再放入新元素;
//数据的访问时间很重要,访问时间距离现在越近,就越不容易被删除;
//就是喜新厌旧,淘汰在缓存里呆的时间最久的元素。在删除元素的时候,只看「时间」这一个维度。
//


//LFU缓存机制(Least Frequently Used)(看访问次数)
//
//在缓存满的时候,删除缓存里使用次数最少的元素,然后在缓存中放入新元素;
//数据的访问次数很重要,访问次数越多,就越不容易被删除,即淘汰访问次数最少的;
//核心思想:先考虑访问次数,在访问次数相同的情况下,再考虑缓存的时间。
//

//核心逻辑就是根据规则进行 key 的排序,然后从尾部删除key


//定义内存数据map
var Data map[string]*Node  //主要为了方便实现和适配两种策略
var Max = 10 //存储数据上限 ,超出存储上限执行对应的策略删除数据

var lfu *LFU

//采用通用节点数据结构体
type Node struct {
   key string
   value string
   count int
   use_time time.Time // 其实不用,直接根据数据在链表位置判断即可
}



//定义lfu记录结构体  双向链表
type LFU_Node struct {
   node *Node  // 当前节点的数据指针
   next *LFU_Node //下一个节点
   pre *LFU_Node// 上一个节点

}



//记录双向链表起止点
type LFU struct {
   count int
   head *LFU_Node
   tail *LFU_Node
   count_list map[int]*LFU_Node //记录 固定次数区块的数据节点开始信息
}

func Init()  {
   lfu = CreateLFU()
   Data = make(map[string]*Node )
}

func Set(key ,value string)  {
   if _,ok:= Data[key] ;ok {
      log.Println("已存在key")
      return
   }
   node := &Node{
      key:key,
      value: value,
      count: 0,
      use_time: time.Now(),
   }
   Data[key] = node
   lfu.Add(node)
   //同时检测是否超过最大值,同时删除
   remove_node := lfu.remove()
   if remove_node != nil {
      delete(Data,remove_node.key)
   }

}


func Get(key string)  string {
   if _,ok:= Data[key] ;ok {
      log.Println(Data[key])
      lfu.Update(Data[key])
   }else {
      log.Println("数据不存在异常")
   }
   return ""

}

func Loop()  {
   lfu.loop()
}

/*
@Time : 2022/2/25 2:49 下午
@Author : yuyunqing
@File : LRU
@Software: GoLand
*/
package lfu_lru

import "log"

/**
 * @Author yuyunqing
 * @Description //TODO 创建一个LFU
 * @Date 2:59 下午 2022/2/25
 **/
func CreateLFU() *LFU {
   //创建链表头尾信息
   head := &LFU_Node{}
   tail := &LFU_Node{}
   head.next = tail
   tail.pre = head
   return &LFU{
      count: 0,
      head: head,
      tail: tail,
      count_list: make(map[int]*LFU_Node),
   }
}

/**
 * @Author yuyunqing
 * @Description //TODO 构造一个LFU 节点
 * @Date 3:02 下午 2022/2/25
 **/
func createLFUNode(node *Node) *LFU_Node {
   return &LFU_Node{
      node: node,
   }
}

/**
 * @Author yuyunqing
 * @Description //TODO 写入节点
 * @Date 3:00 下午 2022/2/25
 **/
func (f *LFU) Add (node *Node) {
   //新增数据默认访问次数为0
   lfu_node := createLFUNode(node)
   old := f.getLfuNodeByTime(0)
   lfu_node.setLfuNode(old)
   f.count_list[0] = lfu_node
   f.count++
   log.Println(f.count)
}

/**
 * @Author yuyunqing
 * @Description //TODO 新增修改时 ,根据访问次数获取 区块起始节点
 * @Date 3:12 下午 2022/2/25
 **/
func (f *LFU ) getLfuNodeByTime(count int) *LFU_Node {
   start :=  f.tail
   if _,ok := f.count_list[count] ;ok {
      //获取访问次数为0的数据区块开头指针
      start = f.count_list[count]
   }else {
      //找到一个比当前count 小的最大的key  简单来说 , 如果count =4   [1,5,6,3] 中找到3
      min_key :=  -1
      for i, _ := range f.count_list {
         if i > min_key  && min_key < count {
            min_key = i
         }
      }
      //当数据不为默认值时 标识找到了 ,否则即为当前值为最小key
      if min_key != -1 {
         start = f.count_list[min_key]
      }
   }
   return start

}

/**
 * @Author yuyunqing
 * @Description //TODO 将当前的节点插入旧节点前面
 * @Date 3:28 下午 2022/2/25
 **/
func (fn *LFU_Node)setLfuNode(old *LFU_Node)  {
   //插入逻辑
   //1当前节点的前置指针,指向旧节点的前置
   //旧节点的前置指针,指向当前节点
   fn.pre = old.pre
   old.pre = fn
   //2当前节点的后置指针,指向旧节点
   fn.next = old
   //3当前节点的前置节点 的 后置指针指向当前节点
   fn.pre.next = fn

   // 0 :    before   <-   old
   // 0 :    before   ->   old
   // 当 new 加入的时候
   // 1 :    before   <- new <- old
   // 1 :    before   ->   old


   // 2 :    before   <- new <-  old
   // 2 :    before(->old)   new ->  old

   // 3 :    before   <- new   <-  old
   // 3 :    before   ->  new  ->  old
}

/**
 * @Author yuyunqing
 * @Description //TODO 用户访问后更新key权重
 * @Date 3:43 下午 2022/2/25
 **/
func (f *LFU) Update( node *Node)  {

   old_lfu_node := f.getLufNodeByNode(node)

   node.count++
   //新建一个节点,并删除原始节点
   lfu_node := createLFUNode(node)

   //找到位置插入新节点
   old := f.getLfuNodeByTime(node.count)
   lfu_node.setLfuNode(old)
   f.count_list[node.count] = lfu_node
   //删除老节点
   f.delete(old_lfu_node ,node.count -1)


}

/**
 * @Author yuyunqing
 * @Description //TODO 根据key 和 访问次数获取 lfu节点
 * @Date 3:48 下午 2022/2/25
 **/
func (f *LFU)getLufNodeByNode(node *Node) *LFU_Node  {
   start := f.head
   if _,ok := f.count_list[node.count] ;ok{
      start =  f.count_list[node.count]
   }else{
      return nil
   }

   for start.node.key != node.key {
      start = start.next
      if start.next == nil {
         return nil
      }
   }
   return start
}

/**
 * @Author yuyunqing
 * @Description //TODO 删除节点
 * @Date 4:44 下午 2022/2/25
 **/
func (f *LFU) delete(fn *LFU_Node , count int)  {
   if fn == nil {
      log.Println("删除节点失败,未获取到可删除节点")
      return
   }

   //从环中删除。
   //当前节点前置的后置指针,指向当前节点的后置
   //当前节点后置的前置指针,指向当前节点的前置
   fn.next.pre = fn.pre
   fn.pre.next = fn.next
   //判断
   if _,ok := f.count_list[count] ;ok{
      start := f.count_list[count]
      // 如果 当前节点不是 count_list中区块的头节点不操作
      if start.node.key ==  fn.node.key {
         //如果下一个节点依旧是对应次数区块
         if fn.next.node != nil && fn.next.node.count == count  {
            f.count_list[count] = fn.next
         }else{
            delete(f.count_list, count)
         }
      }
   }else{
      log.Println("删除节点失败,删除时未发现原始节点count_list区块")
      return
   }

}

/**
 * @Author yuyunqing
 * @Description //TODO 从尾部弹出,过期key
 * @Date 9:30 上午 2022/2/28
 **/
func (f *LFU) remove() *Node {
   if f.count > Max {
      lfu_node := f.tail.pre
      f.tail.pre = lfu_node.pre
      lfu_node.pre.next = f.tail
      //如果 访问次数列表中的 区块头对应了当前节点,则删除
      if f.count_list[lfu_node.node.count].node.key == lfu_node.node.key  {
         delete(f.count_list,lfu_node.node.count )
      }
      log.Println("超过最大限制删除节点:" ,lfu_node.node.key)
      f.count --
      return lfu_node.node
   }else {
      return nil
   }

}


func (f *LFU) loop()  {
   log.Println("循环查看 -----开始")
   start := f.head.next
   for start.next.next != nil {

      log.Println(start.node.key , start.node.value,start.node.count)
      start =start.next
   }

   log.Println(start.node.key , start.node.value,start.node.count)
   log.Println("循环查看 -----结束")
}

package main

import "golangany/lfu_lru"

func main() {
   lfu_lru.Init()
   lfu_lru.Set("zhangsan1","1")
   lfu_lru.Set("zhangsan2","2")
   lfu_lru.Set("zhangsan3","3")
   lfu_lru.Set("zhangsan4","4")
   lfu_lru.Set("zhangsan5","1")
   lfu_lru.Set("zhangsan6","2")
   lfu_lru.Set("zhangsan7","3")
   lfu_lru.Set("zhangsan8","4")
   lfu_lru.Set("zhangsan9","1")
   lfu_lru.Set("zhangsan10","2")
   lfu_lru.Loop()
   lfu_lru.Get("zhangsan3")
   lfu_lru.Loop()
   lfu_lru.Get("zhangsan3")
   lfu_lru.Get("zhangsan4")
   lfu_lru.Get("zhangsan4")
   lfu_lru.Loop()

   lfu_lru.Set("zhangsan11","2")
}
2022/02/28 10:24:43 1
2022/02/28 10:24:43 2
2022/02/28 10:24:43 3
2022/02/28 10:24:43 4
2022/02/28 10:24:43 5
2022/02/28 10:24:43 6
2022/02/28 10:24:43 7
2022/02/28 10:24:43 8
2022/02/28 10:24:43 9
2022/02/28 10:24:43 10
2022/02/28 10:24:43 循环查看 -----开始
2022/02/28 10:24:43 zhangsan10 2 0
2022/02/28 10:24:43 zhangsan9 1 0
2022/02/28 10:24:43 zhangsan8 4 0
2022/02/28 10:24:43 zhangsan7 3 0
2022/02/28 10:24:43 zhangsan6 2 0
2022/02/28 10:24:43 zhangsan5 1 0
2022/02/28 10:24:43 zhangsan4 4 0
2022/02/28 10:24:43 zhangsan3 3 0
2022/02/28 10:24:43 zhangsan2 2 0
2022/02/28 10:24:43 zhangsan1 1 0
2022/02/28 10:24:43 循环查看 -----结束
2022/02/28 10:24:43 &{zhangsan3 3 0 {13870852084508947200 262534 0x117e180}}
2022/02/28 10:24:43 循环查看 -----开始
2022/02/28 10:24:43 zhangsan3 3 1
2022/02/28 10:24:43 zhangsan10 2 0
2022/02/28 10:24:43 zhangsan9 1 0
2022/02/28 10:24:43 zhangsan8 4 0
2022/02/28 10:24:43 zhangsan7 3 0
2022/02/28 10:24:43 zhangsan6 2 0
2022/02/28 10:24:43 zhangsan5 1 0
2022/02/28 10:24:43 zhangsan4 4 0
2022/02/28 10:24:43 zhangsan2 2 0
2022/02/28 10:24:43 zhangsan1 1 0
2022/02/28 10:24:43 循环查看 -----结束
2022/02/28 10:24:43 &{zhangsan3 3 1 {13870852084508947200 262534 0x117e180}}
2022/02/28 10:24:43 &{zhangsan4 4 0 {13870852084508949200 265378 0x117e180}}
2022/02/28 10:24:43 &{zhangsan4 4 1 {13870852084508949200 265378 0x117e180}}
2022/02/28 10:24:43 循环查看 -----开始
2022/02/28 10:24:43 zhangsan4 4 2
2022/02/28 10:24:43 zhangsan3 3 2
2022/02/28 10:24:43 zhangsan10 2 0
2022/02/28 10:24:43 zhangsan9 1 0
2022/02/28 10:24:43 zhangsan8 4 0
2022/02/28 10:24:43 zhangsan7 3 0
2022/02/28 10:24:43 zhangsan6 2 0
2022/02/28 10:24:43 zhangsan5 1 0
2022/02/28 10:24:43 zhangsan2 2 0
2022/02/28 10:24:43 zhangsan1 1 0
2022/02/28 10:24:43 循环查看 -----结束
2022/02/28 10:24:43 11
2022/02/28 10:24:43 超过最大限制删除节点: zhangsan1
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容