流程: 1.从mainCache中获取缓存 2.若获取缓存失败,测从使用者定义的getter方法(数据源)中获取数据,再将数据添加进缓存中。
分布式节点流程: 1、使用一致性hash选择节点 2、从节点中获取缓存值 3、成功则结束、不成功且本地获取缓存值方法不为空则从本地获取缓存值
缓存雪崩:缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤增,引起雪崩。缓存雪崩通常因为缓存服务器宕机、缓存的 key 设置了相同的过期时间等引起。 缓存击穿:一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到 DB ,造成瞬时DB请求量大、压力骤增。 缓存穿透:查询一个不存在的数据,因为不存在则不会写到缓存中,所以每次都会去请求 DB,如果瞬间流量过大,穿透到 DB,导致宕机。
整体思路:
-
确定缓存数据最基础的数据结构、以及缓存淘汰算法(LRU)。
-
设计只读数据结构ByteView用来保存缓存数据,采用字节进行存储可以存储更多样的数据。
-
进一步封装缓存数据结构,使之支撑单机并发。
-
缓存还有一个功能就是当当前缓存取不到想要的数据时,应该考虑从本地数据源或者使用者设置的数据源中获取数据再更新缓存。
- 考虑该包支持设置各种数据源,然后从数据源中取数据; 缺点:数据源很多,难以一一支持。
- 给该缓存数据结构一个结构体成员用于使用该包的人设置当缓存不命中时,如何去数据源中尝试获取数据。 最终采用2. 一个getter字段设置用于数据源中取数据。在进行缓存初始化时设置。这里还设置了一个接口型函数,用于初始化时既可以传函数也可以传实现了该接口的结构体, 与web包里handleFunc有异曲同工之妙。
-
考虑怎么写分布式缓存。
- 分布式缓存很关键一点是怎么找到节点获取数据,这里采用一致性hash算法用于存储寻找节点。
- 分布式缓存不仅可以从别的节点那里获取缓存,也支持别的节点从它这里获取缓存。所以应当设计一个客户端和服务端支持这些操作。httpPool结构体就支持这些操作。
- 将分布式获取缓存与缓存接口解耦,中间用属性进行连接。HttpPool负责充当分布节点的客服端和服务端。而缓存结构体则通过getter字段调用pool的方法获取缓存。
-
singleflight包则用于支持防止缓存击穿