项目地址:https://github.com/patrickmn/go-cache
go-cache是一款类似于memached 的key/value 缓存。它比较适用於单机执行的应用程序。不需要额外部署服务,直接在内存中以map管理缓存的数据。
go-cache实质上就是拥有过期时间并且线程安全的map,可以被多个goroutine安全访问。缓存过期策略为lru策略。
项目主要代码都在cache.go中,并仅需此一个文件即可
代码逻辑清晰。主要是
type Item struct {
Object interface{}
Expiration int64
}
type Cache struct {
*cache
// If this is confusing, see the comment at the bottom of New()
}
type cache struct {
defaultExpiration time.Duration
items map[string]Item
mu sync.RWMutex
onEvicted func(string, interface{})
janitor *janitor
}
type janitor struct {
Interval time.Duration
stop chan bool
}
其中Item存储了每条数据的信息
cache为缓存的总的结构
janitor为启动器。
先说下这个janitor, 用户调用new完成缓存的创立:
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
items := make(map[string]Item)
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
}
newCacheWithJanitor创立了cache对象,函数调用 runJanitor 创立了一个 goroutine完成缓存过期清理。同时使用了小技巧(查看源码注释)保证了缓存清理的goroutine的回收。
而cache提供了缓存的增删改的功能,不再复述,其中用读写锁对map进行多线程保护。
其中一个提高性能小技巧:为了减少defer带来的性能损耗,直接用unlock解锁而不用defer
// TODO: Calls to mu.Unlock are currently not deferred because defer adds ~200 ns (as of go1.)
除了key-value类型的缓存,还支持计数类的缓存。
Sharded:
而此外项目提供的sharded.go,是提供了上述的标准方式的缓存的一种优化。
type unexportedShardedCache struct {
*shardedCache
}
type shardedCache struct {
seed uint32
m uint32
cs []*cache
janitor *shardedJanitor
}
最主要的优化点是用了cache数组(buckets),采用了djb33/djb2的散列方法(// djb2 with better shuffling. 5x faster than FNV with the hash.Hash overhead.)将缓存分布于cache数组中
从而防止了写的过程中对整个缓存的加锁,提高了写入性能。当然也带来了一点内存的消耗和系统复杂度的提升。