分布式缓存部分:
1、缓存淘汰算法
- lfu (least frequently used): 按访问次数淘汰
- lru (least recently used): 按时间淘汰
实现:队列,每次访问都把节点放到队首,淘汰队尾 - lru-k:2q实现
- 访问k次才进入缓存队列,否则按fifo进入临时队列、淘汰
- 缓存队列按照lru淘汰
接口型函数 ref
价值:函数实现了接口(通过在接口方法中调用自己),可以和实现了该接口的结构体一样,当做参数传递。(是go语言函数转接口的常用技巧)
//http包中的例子
http.Handle(..)
http.HandleFunc(..)
//大概原理如下
//定义一个接口
type FooInterface struct {
func foo()
}
//声明一个函数类型FooFunc
type FooFunc func()
//FooFunc实现接口
func(fooFunc FooFunc) foo() {
fooFunc()
}
//使用时,如果有个函数参数为FooInterface
func bar(FooInterface) {}
//可以,用struct,也可以直接用函数进行类型转换
bar(fooInterfaceImplement)
bar(fooFunc(fooFuncImplement))
2、sync.Mutex
groups -->
group(mutex) -->
cache -->
lru(mutex)
3、http服务端,提供访问本地缓存group的接口
4、一致性哈希:
- 哈希函数,同样的key,访问同样节点,提高效率
- 服务器节点数变化,不会引起雪崩
缓存雪崩:缓存同一时刻全部失效,db服务器压力骤增。可能由key同一过期时间,或者缓存宕机等引起
缓存击穿:查询一个存在的key,key过期时,刚好遇到大流量,查到db,压力骤增,
缓存穿透:查询一个不存在的key,因为不会cache,所以每次都查db。如果请求量大,db压力骤增。
- 将key映射到2^32环状空间,节点和数据均哈希到环上,数据放到顺时针遇到的第一个节点。
- 虚拟节点,解决节点过少时的数据倾斜问题。(代价小,只增加map[虚拟节点]真实节点。)
一般哈希函数考虑碰撞率和性能:
- 对于缓存,哈希的结果会对节点数(机器)取模,节点个数影响很大
- 对于完整性校验,碰撞率很影响安全性,crc较差(但编码简单性能好),md5次之,常用256位sha1
crc(循环冗余校验)
5、http客户端
因为服务端与客户端有复用的代码,带来一定理解难度。
api(serveHTTP)
--> group.Get(request)
--> local cache
--> pick peer, peer.Get
server(serveHTTP) 接受请求
--> group.Get(request)
--> local cache
--> pick peer(这里因为peer != self所以不会死循环的再请求)
--> get local & populate cache
group和peerPicker的概念是交叉的,peerPicker负责通过一致性哈希选peer;
group的Get某个key时,如果8001的mainCache查不到,会选peer,然后再查。
可能的TODO:
在api层直接做peerpicker,然后查peer。