天天育儿网,内容丰富有趣,生活中的好帮手!
天天育儿网 > LFU 起夜级李姐

LFU 起夜级李姐

时间:2018-07-24 06:53:26

相关推荐

LFU 起夜级李姐

Go代码实现

type Node struct {key, val, freq intpre, next*Node}type DoubleLink struct {head, tail *Nodelengthint}type LFUCache struct {freq_map map[int]*DoubleLinkkey_map map[int]*Nodecapint}func Constructor(capacity int) LFUCache {return LFUCache{freq_map: map[int]*DoubleLink{},key_map: map[int]*Node{},cap:capacity,}}// 初始化双向链表func InitDoubleLink() *DoubleLink {list := &DoubleLink{head: &Node{},tail: &Node{},length: 0,}list.head.next = list.taillist.tail.pre = list.headreturn list}// 插入双向链表头部func InsertHead(list *DoubleLink, node *Node) {node.next = list.head.nextlist.head.next = nodenode.next.pre = nodenode.pre = list.headlist.length++}// 删除 尾部节点func RemoveTail(list *DoubleLink) (key int) {node := list.tail.prelist.tail.pre = node.prenode.pre.next = list.taillist.length--return node.key}// 删除节点func RemoveNode(list *DoubleLink, node *Node) {node.pre.next = node.nextnode.next.pre = node.prelist.length--}func (this *LFUCache) Get(key int) int {node, ok := this.key_map[key]if !ok {return -1}list := this.freq_map[node.freq]RemoveNode(list, node)// 频率 +1node.freq += 1// 把这个 节点 装入 新频率 的 链表头部list2, ok := this.freq_map[node.freq]if !ok {list2 = InitDoubleLink()this.freq_map[node.freq] = list2}InsertHead(list2, node)return node.val}func (this *LFUCache) Put(key int, value int) {if this.cap == 0 {return}node, ok := this.key_map[key]// 覆盖旧的valueif ok {// 原来的频率 +1list := this.freq_map[node.freq]RemoveNode(list, node)node.freq++node.val = value// 把这个 节点 装入 新频率 的 链表头部list2, ok := this.freq_map[node.freq]if !ok {list2 = InitDoubleLink()this.freq_map[node.freq] = list2}InsertHead(list2, node)} else {// 容量判断 触发淘汰if this.cap < len(this.key_map)+1 {// 从频率 低到高 的找,看是否存在双向链表for i := 1; ; i++ {list, ok := this.freq_map[i]if ok && list.length > 0 {kk := RemoveTail(list)delete(this.key_map, kk)break}}}// 装入keymapnode = &Node{key: key,val: value,freq: 1, // 新增的频率都是1}this.key_map[key] = node// 装入 freq_map 频率=1 的 链表头部list, ok := this.freq_map[1]if !ok {list = InitDoubleLink()this.freq_map[1] = list}InsertHead(list, node)}}

面试相关

对比前文,会发现,LFU的实现远远比LRU的复杂,如果只是写伪代码的话,或许还好些,但是要写出能够通过leetcode提交的代码,大家也看到了,并不容易。所以我认为,对于LFU,知道这个算法的逻辑,以及如何优化成O(1)就OK了,没有必要纯手写。

Redis中的LFU

没有意外,redis中的lfu实现依然不是上述代码所展现的那种方式。

问题1:上述代码是实打实的记录了使用频率,这很占用内存(站在redis的视角)

问题2:可能一段时间某个key使用频率超高,但后续再不使用,这样这些按理说应该淘汰的key因为频率太高,一直淘汰不了。

Redis中的LFU算法利用的依然是redisObject中的lru字段(24bit)

前情回顾:lru这个字段在Redis的LRU策略中,用作记录时间戳的。

配置

lfu-log-factor 10lfu-decay-time 1

lfu-log-factor可以调整计数器counter(后8位)的增长速度,lfu-log-factor越大,counter增长的越慢。

lfu-decay-time是一个以分钟为单位的数值,可以调整counter的减少速度

把lru字段的24bit分成两部分

高16位用来记录访问时间(单位为分钟)低8位用来记录访问频率,简称counter(8位只能代表255)

counter并不是一个简单的线性计数器,而是用基于概率的对数计数器来实现。

别管怎么算的,就记结论:默认server.lfu_log_factor为10的情况下,8 bits的counter可以表示大约1百万的访问频率。

这就把问题1解决了。

为了解决问题2,redis提供了衰减因子server.lfu_decay_time,其单位为分钟,

如果一个key长时间没有访问,那么它的计数器counter就要减少,减少的值由衰减因子来控制。

默认为1的情况下也就是N分钟内没有访问,counter就要减N。

LFU信息的采样方式与LRU的类似。

如果觉得《LFU 起夜级李姐》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。