geektutu / blog

极客兔兔的博客,Coding Coding 创建有趣的开源项目。
https://geektutu.com
Apache License 2.0
168 stars 21 forks source link

动手写分布式缓存 - GeeCache第一天 LRU 缓存淘汰策略 | 极客兔兔 #62

Open geektutu opened 4 years ago

geektutu commented 4 years ago

https://geektutu.com/post/geecache-day1.html

7天用 Go语言/golang 从零实现分布式缓存 GeeCache 教程(7 days implement golang distributed cache from scratch tutorial),动手写分布式缓存,参照 groupcache 的实现。本文介绍常用的三种缓存淘汰(失效)算法:先进先出(FIFO),最少使用(LFU) 和 最近最少使用(LRU),并实现 LRU 算法和相应的测试代码。

jxy90 commented 2 years ago

@OwenLiGithub 兔兔,在测试怎么找不到New 方法,后面都可以调用这些方法了。。。。为什么跑的时候还会undefined

我也是运行测试的时候,出现/lru_test.go:15:9: undefined: New. 这个情况,你解决了么?

在引入gomod 之后已解决. go mod init example

Xianhyy commented 2 years ago

内存大小不能用len来表示吧,是不是应该用sizeof来获取

csxgogogo commented 2 years ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

ts-cao commented 2 years ago

@demoManito

@geektutu @UnbearableFate 因为 Go 没有泛型的机制,所以只能用 interface{} 来代替任意的类型,官方的说法是最早在 1.17 支持,不过我对这个时间点比较悲观,可能得等到 2.0 版本了。

1.17 已经支持了啊

1.18已经支持了

csxgogogo commented 2 years ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

ts-cao commented 2 years ago

@Xianhyy 内存大小不能用len来表示吧,是不是应该用sizeof来获取

在go语言中对于string,用len()返回的就是字节数量

OOOOlh commented 2 years ago

请教一下, 每次更新当前内存大小时,如value.Len()。这里的Len()的实现: //占用内存的大小 func (c Cache) Len() int { return c.ll.Len() } 返回的是链表的元素数量吧,根据c.ll.Len()源码: // Len returns the number of elements of list l. // The complexity is O(1). func (l List) Len() int { return l.len } 但每次内存大小更新不应该是字节大小吗,链表元素数量和字节不是对等的关系。为什么能够相加减呢。

如删除操作中:c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len()) len(kv.key)得到的是字符串的字节数,而 kv.value.Len()得到的是链表的长度。后者能代表字节长度吗?

csxgogogo commented 2 years ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

zhang-weiyu commented 2 years ago

@OOOOlh 请教一下, 每次更新当前内存大小时,如value.Len()。这里的Len()的实现: //占用内存的大小 func (c Cache) Len() int { return c.ll.Len() } 返回的是链表的元素数量吧,根据c.ll.Len()源码: // Len returns the number of elements of list l. // The complexity is O(1). func (l List) Len() int { return l.len } 但每次内存大小更新不应该是字节大小吗,链表元素数量和字节不是对等的关系。为什么能够相加减呢。

如删除操作中:c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len()) len(kv.key)得到的是字符串的字节数,而 kv.value.Len()得到的是链表的长度。后者能代表字节长度吗?

你的理解不太对, func (c Cache) Len()虽然实现了Value接口, 但不是用这个, 只是名字一样罢了, 作用就是返回当前链表元素数量。而kv.value.Len()是这样的,我们要求存储在链表里的元素的value是Value类型,也就是实现了Value接口,也就是实现了Len方法,kv.value.Len()这个Len是所存数据的Len方法,而不是func (c Cache) Len()这个实现

ueueQ commented 2 years ago

这一节除了看兔兔讲解的,强烈建议去看看源码:http://godoc.org/github.com/hashicorp/golang-lru

csxgogogo commented 1 year ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

RookieCoder99 commented 1 year ago

有个问题,就是计算entry占用字节数大小的时候,采用key的大小加value的大小,这个和entry大小是相等的吗,不会有内存对齐的问题吗?

csxgogogo commented 1 year ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

csxgogogo commented 1 year ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

lovepyu commented 1 year ago

@yinhuanyi 测试用例好像写错了,是!ok才能打印key2的cache miss

func TestGet(t *testing.T) {
  // ...

  if _, ok := lru.Get("key2"); !ok {
      t.Fatalf("cache miss key2 failed")
  }
}

作者的意思应该是如果在没有Add("Key2")的情况下Get("key2")成功了,才是错误的吧,正常Get方法中有判断key是否存在的。

MaXiaoYang6 commented 1 year ago

这里的LRU,队首队尾的叫法感觉非常难受,按照正常逻辑,先放进去的元素 FRONT 应该是队首才对. 另, 写 LRU 是力扣的必刷题,建议自己写一遍,不用标准库 嘻嘻

csxgogogo commented 1 year ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

MaXiaoYang6 commented 1 year ago

@ts-cao

@Xianhyy 内存大小不能用len来表示吧,是不是应该用sizeof来获取

在go语言中对于string,用len()返回的就是字节数量

一派胡言,这里应该是作者写错了,在GO中,len()返回的就是字符串的长度,应该用sizeof来获取字符串所占用的字节数.

GuYith commented 1 year ago

@MaXiaoYang6

@ts-cao

@Xianhyy 内存大小不能用len来表示吧,是不是应该用sizeof来获取

在go语言中对于string,用len()返回的就是字节数量

一派胡言,这里应该是作者写错了,在GO中,len()返回的就是字符串的长度,应该用sizeof来获取字符串所占用的字节数.

可以看看Go中关于len()的描述,里面明确写了len()参数类型为string时返回字节数

func len(v Type) int The len built-in function returns the length of v, according to its type:

Array: the number of elements in v. Pointer to array: the number of elements in *v (even if v is nil). Slice, or map: the number of elements in v; if v is nil, len(v) is zero. String: the number of bytes in v. Channel: the number of elements queued (unread) in the channel buffer; if v is nil, len(v) is zero. For some arguments, such as a string literal or a simple array expression, the result can be a constant. See the Go language specification's "Length and capacity" section for details.

len on pkg.go.dev

csxgogogo commented 1 year ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

csxgogogo commented 1 year ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

gallifreyCar commented 1 year ago

@MaXiaoYang6 源码写了len()对于不同类型返回的值不同,对于String,返回的就是字节数 String: the number of bytes in v.

Fencent commented 10 months ago

@OOOOlh 请教一下, 每次更新当前内存大小时,如value.Len()。这里的Len()的实现: //占用内存的大小 func (c Cache) Len() int { return c.ll.Len() } 返回的是链表的元素数量吧,根据c.ll.Len()源码: // Len returns the number of elements of list l. // The complexity is O(1). func (l List) Len() int { return l.len } 但每次内存大小更新不应该是字节大小吗,链表元素数量和字节不是对等的关系。为什么能够相加减呢。

如删除操作中:c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len()) len(kv.key)得到的是字符串的字节数,而 kv.value.Len()得到的是链表的长度。后者能代表字节长度吗?

你说的这个Len方法是外界获取缓存数量时用的,而非内存更新时用的,只是同名而已。元素更新时调用的Len是对应的value类型自己实现的方法,例如作者在测试用例中定义的String类型,其实现了自己的Len方法

csxgogogo commented 10 months ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

dengyunsheng250 commented 9 months ago

兔兔大佬考虑实现LRU -K算法不?

csxgogogo commented 9 months ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ

csxgogogo commented 6 months ago

gsdg54sdg5s4d5gトオジビタからロピカなフアやャクヤなどがミドっぱくラバニラサ