From a4414066fa4f54833f6d6ed5b01a52d49aaf5ecf Mon Sep 17 00:00:00 2001 From: gzdaijie Date: Tue, 11 Feb 2020 22:10:37 +0800 Subject: [PATCH] add geecache-day1 doc --- README.md | 2 +- gee-cache/doc/geecache-day1.md | 251 +++++++++++++++++++++++ gee-cache/doc/geecache-day1/lru.jpg | Bin 0 -> 12957 bytes gee-cache/doc/geecache-day1/lru_logo.jpg | Bin 0 -> 5272 bytes gee-cache/doc/geecache.md | 5 +- 5 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 gee-cache/doc/geecache-day1.md create mode 100755 gee-cache/doc/geecache-day1/lru.jpg create mode 100755 gee-cache/doc/geecache-day1/lru_logo.jpg diff --git a/README.md b/README.md index cdc75ff..28e085d 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ [GeeCache](https://geektutu.com/post/geecache.html) 是一个模仿 [groupcache](https://github.com/golang/groupcache) 实现的分布式缓存系统 -- 第一天:LRU 缓存策略 | [Code](gee-cache/day1-lru) +- [第一天:LRU 缓存淘汰策略](https://geektutu.com/post/geecache-day1.html) | [Code](gee-cache/day1-lru) - 第二天:单机并发缓存 | [Code](gee-cache/day2-single-node) - 第三天:HTTP 服务端 | [Code](gee-cache/day3-http-server) - 第四天:一致性哈希(Hash) | [Code](gee-cache/day4-consistent-hash) diff --git a/gee-cache/doc/geecache-day1.md b/gee-cache/doc/geecache-day1.md new file mode 100644 index 0000000..f5330d8 --- /dev/null +++ b/gee-cache/doc/geecache-day1.md @@ -0,0 +1,251 @@ +--- +title: 动手写分布式缓存 - GeeCache第一天 LRU 缓存淘汰策略 +date: 2020-02-11 22:00:00 +description: 7天用 Go语言/golang 从零实现分布式缓存 GeeCache 教程(7 days implement golang distributed cache from scratch tutorial),动手写分布式缓存,参照 groupcache 的实现。本文介绍常用的三种缓存淘汰(失效)算法:先进先出(FIFO),最少使用(LFU) 和 最近最少使用(LRU),并实现 LRU 算法和相应的测试代码。 +tags: +- Go +nav: 从零实现 +categories: +- 分布式缓存 - GeeCache +keywords: +- Go语言 +- 从零实现分布式缓存 +- 动手写分布式缓存 +- LRU +- 缓存失效 +image: post/geecache-day1/lru_logo.jpg +github: https://github.com/geektutu/7days-golang +--- + + +本文是[7天用Go从零实现分布式缓存GeeCache教程系列](https://geektutu.com/post/geecache.html)的第一篇。 + +- 介绍常用的三种缓存淘汰(失效)算法:FIFO,LFU 和 LRU +- 实现 LRU 缓存淘汰算法,**代码约80行** + +## 1 FIFO/LFU/LRU 算法简介 + +GeeCache 的缓存全部存储在内存中,内存是有限的,因此不可能无限制地添加数据。假定我们设置缓存能够使用的内存大小为 N,那么在某一个时间点,添加了某一条缓存记录之后,占用内存超过了 N,这个时候就需要从缓存中移除一条或多条数据了。那移除谁呢?我们肯定希望尽可能移除“没用”的数据,那如何判定数据“有用”还是“没用”呢? + +### 1.1 FIFO(First In First Out) + +先进先出,也就是淘汰缓存中最老(最早添加)的记录。FIFO 认为,最早添加的记录,其不再被使用的可能性比刚添加的可能性大。这种算法的实现也非常简单,创建一个队列,新增记录添加到队尾,每次内存不够时,淘汰队首。但是很多场景下,部分记录虽然是最早添加但也最常被访问,而不得不因为呆的时间太长而被淘汰。这类数据会被频繁地添加进缓存,又被淘汰出去,导致缓存命中率降低。 + +### 1.2 LFU(Least Frequently Used) + +最少使用,也就是淘汰缓存中访问频率最低的记录。LFU 认为,如果数据过去被访问多次,那么将来被访问的频率也更高。LFU 的实现需要维护一个按照访问次数排序的队列,每次访问,访问次数加1,队列重新排序,淘汰时选择访问次数最少的即可。LFU 算法的命中率是比较高的,但缺点也非常明显,维护每个记录的访问次数,对内存的消耗是很高的;另外,如果数据的访问模式发生变化,LFU 需要较长的时间去适应,也就是说 LFU 算法受历史数据的影响比较大。例如某个数据历史上访问次数奇高,但在某个时间点之后几乎不再被访问,但因为历史访问次数过高,而迟迟不能被淘汰。 + +### 1.3 LRU(Least Recently Used) + +最近最少使用,相对于仅考虑时间因素的 FIFO 和仅考虑访问频率的 LFU,LRU 算法可以认为是相对平衡的一种淘汰算法。LRU 认为,如果数据最近被访问过,那么将来被访问的概率也会更高。LRU 算法的实现非常简单,维护一个队列,如果某条记录被访问了,则移动到队尾,那么队首则是最近最少访问的数据,淘汰该条记录即可。 + +## 2 LRU 算法实现 + +### 2.1 核心数据结构 + +![implement lru algorithm with golang](geecache-day1/lru.jpg) + +这张图很好地表示了 LRU 算法最核心的 2 个数据结构 + +- 绿色的是字典(map),存储键和值的映射关系。这样根据某个键(key)查找对应的值(value)的复杂是`O(1)`,在字典中插入一条记录的复杂度也是`O(1)`。 +- 红色的是双向链表(double linked list)实现的队列。将所有的值放到双向链表中,这样,当访问到某个值时,将其移动到队尾的复杂度是`O(1)`,在队尾新增一条记录以及删除一条记录的复杂度均为`O(1)`。 + +接下来我们创建一个包含字典和双向链表的结构体类型 Cache,方便实现后续的增删查改操作。 + +[day1-lru/geecache/lru/lru.go - github](https://github.com/geektutu/7days-golang/tree/master/gee-cache/day1-lru/geecache/lru) + +```go +package lru + +import "container/list" + +// Cache is a LRU cache. It is not safe for concurrent access. +type Cache struct { + maxBytes int64 + nbytes int64 + ll *list.List + cache map[string]*list.Element + // optional and executed when an entry is purged. + OnEvicted func(key string, value Value) +} + +type entry struct { + key string + value Value +} + +// Value use Len to count how many bytes it takes +type Value interface { + Len() int +} +``` + +- 在这里我们直接使用 Go 语言标准库实现的双向链表`list.List`。 +- 字典的定义是 `map[string]*list.Element`,键是字符串,值是双向链表中对应节点的指针。 +- `maxBytes` 是允许使用的最大内存,`nbytes` 是当前已使用的内存,`OnEvicted` 是某条记录被移除时的回调函数,可以为 nil。 +- 键值对 `entry` 是双向链表节点的数据类型,在链表中仍保存每个值对应的 key 的好处在于,淘汰队首节点时,需要用 key 从字典中删除对应的映射。 +- 为了通用性,我们允许值是实现了 `Value` 接口的任意类型,该接口只包含了一个方法 `Len() int`,用于返回值所占用的内存大小。 + + +方便实例化 `Cache`,实现 `New()` 函数: + +```go +// New is the Constructor of Cache +func New(maxBytes int64, onEvicted func(string, Value)) *Cache { + return &Cache{ + maxBytes: maxBytes, + ll: list.New(), + cache: make(map[string]*list.Element), + OnEvicted: onEvicted, + } +} +``` + +### 2.2 查找功能 + +查找主要有 2 个步骤,第一步是从字典中找到对应的双向链表的节点,第二步,将该节点移动到队尾。 + +```go +// Get look ups a key's value +func (c *Cache) Get(key string) (value Value, ok bool) { + if ele, ok := c.cache[key]; ok { + c.ll.MoveToFront(ele) + kv := ele.Value.(*entry) + return kv.value, true + } + return +} +``` + +- 如果键对应的链表节点存在,则将对应节点移动到队尾,并返回查找到的值。 +- `c.ll.MoveToFront(ele)`,即将链表中的节点 `ele` 移动到队尾(双向链表作为队列,队首队尾是相对的,在这里约定 front 为队尾) + +### 2.3 删除 + +这里的删除,实际上是缓存淘汰。即移除最近最少访问的节点(队首) + +```go +// RemoveOldest removes the oldest item +func (c *Cache) RemoveOldest() { + ele := c.ll.Back() + if ele != nil { + c.ll.Remove(ele) + kv := ele.Value.(*entry) + delete(c.cache, kv.key) + c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len()) + if c.OnEvicted != nil { + c.OnEvicted(kv.key, kv.value) + } + } +} +``` + +- `c.ll.Back()` 取到队首节点,从链表中删除。 +- `delete(c.cache, kv.key)`,从字典中`c.cache`删除该节点的映射关系。 +- 更新当前所用的内存 `c.nbytes`。 +- 如果回调函数 `OnEvicted` 不为 nil,则调用回调函数。 + +### 2.4 新增/修改 + +```go +// Add adds a value to the cache. +func (c *Cache) Add(key string, value Value) { + if ele, ok := c.cache[key]; ok { + c.ll.MoveToFront(ele) + kv := ele.Value.(*entry) + kv.value = value + return + } + ele := c.ll.PushFront(&entry{key, value}) + c.cache[key] = ele + c.nbytes += int64(len(key)) + int64(value.Len()) + + for c.maxBytes != 0 && c.maxBytes < c.nbytes { + c.RemoveOldest() + } +} +``` + +- 如果键存在,则更新对应节点的值,并将该节点移到队尾。 +- 不存在则是新增场景,首先队尾添加新节点 `&entry{key, value}`, 并字典中添加 `key` 和节点的映射关系。 +- 更新 `c.nbytes`,如果超过了设定的最大值 `c.maxBytes`,则移除最少访问的节点。 + +最后,为了方便测试,我们实现 `Len()` 用来获取添加了多少条数据。 + +```go +// Len the number of cache entries +func (c *Cache) Len() int { + return c.ll.Len() +} +``` + +## 3 测试 + +例如,我们可以尝试添加几条数据,测试 `Get` 方法 + +[day1-lru/geecache/lru/lru_test.go - github](https://github.com/geektutu/7days-golang/tree/master/gee-cache/day1-lru/geecache/lru) + +```go +type String string + +func (d String) Len() int { + return len(d) +} + +func TestGet(t *testing.T) { + lru := New(int64(0), nil) + lru.Add("key1", String("1234")) + if v, ok := lru.Get("key1"); !ok || string(v.(String)) != "1234" { + t.Fatalf("cache hit key1=1234 failed") + } + if _, ok := lru.Get("key2"); ok { + t.Fatalf("cache miss key2 failed") + } +} +``` + +测试,当使用内存超过了设定值时,是否会触发“无用”节点的移除: + +```go +func TestRemoveoldest(t *testing.T) { + k1, k2, k3 := "key1", "key2", "k3" + v1, v2, v3 := "value1", "value2", "v3" + cap := len(k1 + k2 + v1 + v2) + lru := New(int64(cap), nil) + lru.Add(k1, String(v1)) + lru.Add(k2, String(v2)) + lru.Add(k3, String(v3)) + + if _, ok := lru.Get("key1"); ok || lru.Len() != 2 { + t.Fatalf("Removeoldest key1 failed") + } +} +``` + +测试回调函数能否被调用: + +```go +func TestOnEvicted(t *testing.T) { + keys := make([]string, 0) + callback := func(key string, value Value) { + keys = append(keys, key) + } + lru := New(int64(10), callback) + lru.Add("key1", String("123456")) + lru.Add("k2", String("k2")) + lru.Add("k3", String("k3")) + lru.Add("k4", String("k4")) + + expect := []string{"key1", "k2"} + + if !reflect.DeepEqual(expect, keys) { + t.Fatalf("Call OnEvicted failed, expect keys equals to %s", expect) + } +} +``` + +## 附 推荐阅读 + +- [Go 语言简明教程](https://geektutu.com/post/quick-golang.html) +- [Go Test 单元测试简明教程](https://geektutu.com/post/quick-go-test.html) +- [list 官方文档 - golang.org](https://golang.org/pkg/container/list/) \ No newline at end of file diff --git a/gee-cache/doc/geecache-day1/lru.jpg b/gee-cache/doc/geecache-day1/lru.jpg new file mode 100755 index 0000000000000000000000000000000000000000..db90cd8386de9e36b59fa89b6328146242d08ff3 GIT binary patch literal 12957 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 literal 0 HcmV?d00001 diff --git a/gee-cache/doc/geecache.md b/gee-cache/doc/geecache.md index 21b2f0d..fc79090 100644 --- a/gee-cache/doc/geecache.md +++ b/gee-cache/doc/geecache.md @@ -1,7 +1,7 @@ --- title: 7天用Go从零实现分布式缓存GeeCache date: 2020-02-08 01:00:00 -description: 7天用 用 Go语言/golang 从零实现分布式缓存 GeeCache 教程(7 days implement golang distributed cache from scratch tutorial),动手写分布式缓存,参照 groupcache 的实现。功能包括单机/分布式缓存,LRU (Least Recently Used) 缓存策略,防止缓存击穿、一致性哈希(Consistent Hash),protobuf 通信等。 +description: 7天用 Go语言/golang 从零实现分布式缓存 GeeCache 教程(7 days implement golang distributed cache from scratch tutorial),动手写分布式缓存,参照 groupcache 的实现。功能包括单机/分布式缓存,LRU (Least Recently Used) 缓存策略,防止缓存击穿、一致性哈希(Consistent Hash),protobuf 通信等。 tags: - Go nav: 从零实现 @@ -58,7 +58,7 @@ github: https://github.com/geektutu/7days-golang ## 3 目录 -- 第一天:LRU 缓存策略 | [Code - Github](https://github.com/geektutu/7days-golang/blob/master/gee-cache/day1-lru) +- [第一天:LRU 缓存淘汰策略](https://geektutu.com/post/geecache-day1.html) | [Code - Github](https://github.com/geektutu/7days-golang/blob/master/gee-cache/day1-lru) - 第二天:单机并发缓存 | [Code - Github](https://github.com/geektutu/7days-golang/blob/master/gee-cache/day2-single-node) - 第三天:HTTP 服务端 | [Code - Github](https://github.com/geektutu/7days-golang/blob/master/gee-cache/day3-http-server) - 第四天:一致性哈希(Hash) | [Code - Github](https://github.com/geektutu/7days-golang/blob/master/gee-cache/day4-consistent-hash) @@ -69,4 +69,5 @@ github: https://github.com/geektutu/7days-golang ## 附 推荐阅读 - [Go 语言简明教程](https://geektutu.com/post/quick-golang.html) +- [Go Test 单元测试简明教程](https://geektutu.com/post/quick-go-test.html) - [Go Protobuf 简明教程](https://geektutu.com/post/quick-go-protobuf.html) \ No newline at end of file