go高效缓存框架教你实现

Go缓存框架–手把手带你实现


源文章地址


引言:

首先简单的介绍一下这款缓存框架

  • 它高效,功能强大,支持多线程
  • 它是一个非常好的学习开源项目.这篇文章我将会介绍我开发时候的思路
  • 它非常适合学习Go的特性.笔者使用channel 实现同步异步的获取值操作. 在设置和删除的时候是异步的.且不会发生并发问题. 你甚至可以修改源代码 自己写一些缓存算法来自己用.并且你不需要考虑加锁的问题.
  • 我实现了LRU(最近最久未使用算法)和LFU(最近最少使用算法)
  • 当然它也支持使过期时间.它的开发空间还很大.是一款非常棒非常棒的学习框架. 你甚至可以加入客户端服务端的代码 只要OK你都可以Request 然后也可以分布式 发挥你的想想
  • 有一个goroutines 监听管道 cache向管道里写入命令. 执行者依次执行命令就可以了. redis也是一个单线程的执行器.
  • 这里也使用了强大的缓存管理者
  • 如果你希望提高你的Go编程功底.那么看完这篇文章吧!
  • 如果你有好的想法,但是觉得不太会你可以私信我 我们一起探讨
  • 项目已经开源放到github上, 并且有很详细的使用说明, 当然它也恨简单
  • github源代码地址 <- 戳我
  • 注: 再说一便我会把完整的代码思想都写出来 包括写完后测试优化 我将近优化了之前5倍的速度

在这里插入图片描述


实现之初

为什么想着实现一个缓存框架呢? 笔者最近也一直在看缓存. 然后也没有去实践.然后想着看看网上的go缓存框架.github上有一款. 我窃喜的阅读了源代码. 它的内容太少了 而且没有太多的学习的地方.所以我就想着自己实现一个. 首先我一开始想先从缓存的策略开始写起. 我的打算是先写两个 一个是LRU和LFU.
LRU算法是比较好实现, 所以我先从难的开始写起

LFU算法

如果这里不想看了 可以跳后面其实没啥关联
当然在LeetCode这个刷题网站就有这道题. 笔者是实现出来以后先去检测通过了再开始往下继续的
在这里插入图片描述
下面是笔者的A题记录
基本都在 %95以上
在这里插入图片描述
可以大致的讲一下思路: 如果有需要的小伙伴可以私我一下, 我单独写一篇分析LFU算法的也可以.
这里的代码是O(1)的时间复杂度
首先缓存你应该使用Hash表. 这里Hash表存key和 value value是一个结点的地址 结点里有数据的实例封装了一下.
LFU的淘汰策略是使用最少的, 那其实这里思想很简单. 有一个变量去记录了它的访问次数, 一开始是1, get以后直接+1. 然后需要淘汰的时候淘汰最少的.
基于这样的一个涉及思想 笔者一开始最先相先想到的是优先队列 最小堆.但是这样的时间复杂度就是O(logn)
伪O(1)的算法: 这个是再使用一个map 这个map的key是访问次数, 键是一个双向链表. 然后需要淘汰了.从最少的起 开始是1 for循环找到第一个不是空链的尾结点删除(因为先进入的是头结点. 尾结点说明它好久没访问了) 这样其实最好的情况就是O(1)
但是这样的情况其实是有断层的.最坏的情况 访问次数1-n的链表都是空的 这样时间复杂度就上升到了O(n)
O(1)的算法 基于上面的断层问题做处理就行了. 这里换一种数据结构. 用一个双向链表,这个双向链表的结点还是一个双向链表. 链表链.每次只从第一个链表的尾结点删除. 如果链表都是空了 删除这个链表 这样总能保证第一个链表里面是访问最少的.
过多的细节这里不再赘述

源代码如下



type LFUCache struct {
   
	capacity int
	size int
	elements         map[int]*doublyListNode
	chain LFUChain
}

type LFUChain struct {
   
	firstLinkedList  *DoublyLinkedList
	lastLinkedList   *DoublyLinkedList
}



func (lc *LFUCache) Size() int {
   
	return lc.size
}


func Constructor(capacity int) LFUCache {
   
	lfuCache := LFUCache{
   
		capacity: capacity,
		size:     0,
		elements: make(map[int]*doublyListNode, (capacity<<1|capacity)+1),
		chain: LFUChain{
   
			firstLinkedList: New(0),
			lastLinkedList:  New(0),
		},
	}
	lfuCache.chain.firstLinkedList.next = lfuCache.chain.lastLinkedList
	lfuCache.chain.lastLinkedList.prev = lfuCache.chain.firstLinkedList
	return lfuCache
}


func (lc *LFUCache) Get(key int) int {
   
	node, ok := lc.elements[key]
	if !ok {
   
		return -1
	}

	freqInc(node)
	return node.data.(int)
}



func (lc *LFUCache) Put(key int, value int)  {
   
	if lc.capacity <= 0 {
   
		return
	}

	node , ok := lc.elements[key]
	if ok {
   
		node.data = value
		freqInc(node)
	} else {
   
		if lc.Size() >= lc.capacity {
   
			listNodeChain := lc.chain.firstLinkedList.next
			delNode, _ := listNodeChain.RemoveTail()
			delete(lc.elements, delNode.key.(int))
			lc.size--

			if listNodeChain.IsEmpty() {
   
	
				listNodeChain.LeaveForChain()
	
			}
		}
		lfuNode := &doublyListNode{
   
			key:key,
			data:value,
			freq:1,
		}
		lc.elements[key] = lfuNode
		if lc.chain.firstLinkedList.next.freq != 1 {
   
			tempList := New(1)
			tempList.AddFirst(lfuNode)
			insertChain(lc.chain.firstLinkedList, tempList)
		} else {
   
			lc.chain.firstLinkedList.next.AddFirst(lfuNode)
		}
		lc.size++
	}


}

func freqInc(node *doublyListNode)  {
   
	parentList := node.parent
	node.LeaveForList()
	
	nextList := parentList.next

	tempParent := parentList.prev
	if parentList.IsEmpty() {
   

		parentList.LeaveForChain()
		parentList = tempParent
	}
	node.freq++
	if nextList.freq != node.freq {
   
		newChain := New(node.freq)
		insertChain(parentList, newChain)
		newChain.AddFi
  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值