freecache
zhuanlan.zhihu.com/p/487455942
选择原因
-
zero gc, 通过slice实现map 减少指针
-
支持设置单个key的过期时间
FreeCache avoids GC overhead by reducing the number of pointers. No matter how many entries stored in it, there are only 512 pointers. The data set is sharded into 256 segments by the hash value of the key. Each segment has only two pointers, one is the ring buffer that stores keys and values, the other one is the index slice which used to lookup for an entry. Each segment has its own lock, so it supports high concurrent access.
有256个segment,每个segment有256个slot ,a slot is an array of entry pointers ordered by hash16 value ,the entry can be looked up by hash value of the key.
const (
// segmentCount represents the number of segments within a freecache instance.
segmentCount = 256
// segmentAndOpVal is bitwise AND applied to the hashVal to find the segment id.
segmentAndOpVal = 255
minBufSize = 512 * 1024
)
// Cache is a freecache instance.
type Cache struct {
locks [segmentCount]sync.Mutex
segments [segmentCount]segment
}
// a segment contains 256 slots, a slot is an array of entry pointers ordered by hash16 value
// the entry can be looked up by hash value of the key.
type segment struct {
rb RingBuf // ring buffer that stores data
segId int
_ uint32
missCount int64
hitCount int64
entryCount int64
totalCount int64 // number of entries in ring buffer, including deleted entries.
totalTime int64 // used to calculate least recent used entry.
timer Timer // Timer giving current time
totalEvacuate int64 // used for debug
totalExpired int64 // used for debug
overwrites int64 // used for debug
touched int64 // used for debug
vacuumLen int64 // up to vacuumLen, new data can be written without overwriting old data.
slotLens [256]int32 // The actual length for every slot.
slotCap int32 // max number of entry pointers a slot can hold.
slotsData []entryPtr // shared by all 256 slots
}
// entry pointer struct points to an entry in ring buffer
type entryPtr struct {
offset int64 // entry offset in ring buffer
hash16 uint16 // entries are ordered by hash16 in a slot.
keyLen uint16 // used to compare a key
reserved uint32
}
set
// If the key is larger than 65535 or value is larger than 1/1024 of the cache size, // the entry will not be written to the cache.
func (cache *Cache) Set(key, value []byte, expireSeconds int) (err error) {
hashVal := hashFunc(key)
segID := hashVal & segmentAndOpVal
cache.locks[segID].Lock()
err = cache.segments[segID].set(key, value, hashVal, expireSeconds)
cache.locks[segID].Unlock()
return
}
slotId := uint8(hashVal >> 8) // uint8转换的数 超过8位 则 取低8位
hash16 := uint16(hashVal >> 16)
slot := seg.getSlot(slotId)
idx, match := seg.lookup(slot, hash16, key)// 二分查找
expire 逻辑
Get 或者 eviction 的时候判断, 惰性删除
eviction
近似LRU
set 的时候触发