合作机构:阿里云 / 腾讯云 / 亚马逊云 / DreamHost / NameSilo / INWX / GODADDY / 百度统计
你好哇!我是小翔。之前写了三篇 #Golang 并发编程 的文章了,这次来换换口味,开个 手撕源码 的新坑!一起来扒一扒 Go 语言高性能 local cache 库 bigcache,看看能不能把开源大佬们的骚操作带到项目里去装一装(?)
个人认为学习开源项目的收益:
缓存是系统提升并发能力、降低时延的利器,根据存储介质和使用场景,我们一般又会使用本地缓存与分布式缓存两种手段。本地缓存一般是在进程内的,最简单的,用 go 的 sync.Map 就能实现一个简单的并发安全的本地缓存了。常见的,将一些静态的、配置类的数据放置在本地缓存中,能有效降低到下游存储的压力。分布式缓存一般会用 redis 或 memcached 等分布式内存数据库来实现,能做到分布式、无状态。这次先研究下 bigcache 后续有机会再挖一挖这里。
bigcache 的开发者是 allegro,是波兰的一个电商网站,参考资料中给出了他们的技术博客的原文,文中详细描述了他们问题的背景以及思考,值得研究。他们的需求主要是:
开发团队经过了一番对比,选择了 go 语言(高并发度、带内存管理安全性比 C/C++ 好),抛弃了分布式缓存组件(redis/memcached/couchbase),主要理由是多一跳网络开销。这里我表示怀疑,P999 400ms 的时延其实不至于担心到 redis 网络那点时间,分布式环境下 local cache 不同机器间的数据不一致带来的 cache miss 可能更蛋疼。 最终开发团队选择了实现一个支持以下特性的内存缓存库:
设计上如何做到并发安全呢?最简单的思路就是给 map 上一把 sync.RWMutex 即读写锁。然而当缓存项过多时,并发请求会造成锁冲突,因此需要降低锁粒度。bigcache 采用了分布式系统里常用的 sharding 思路,即将一个大 map 拆分成 N 个小 map,我们称为一个 shard(分片)
如 bigcache.go 的声明,我们初始化得到的 BigCache,核心实际上是一个 []*cacheShard,缓存的写入、淘汰等核心逻辑都在 cacheShard 中了
type BigCache struct {
shards []*cacheShard
lifeWindow uint64
clock clock
hash Hasher
config Config
shardMask uint64
close chan struct{}
}
TOP