ristretto 参数
我在使用ristretto时,对于参数的设置有些疑问。主要是 NumCounters ,MaxCost 分别表示什么含义,以及如何确定其数值的问题。
在此记录并分享一下,欢迎各位批评指正,谢谢
官方的指导
先看一下官方的例子以及相关的解释:
func main() {
cache, err := ristretto.NewCache(&ristretto.Config{
NumCounters: 1e7, // number of keys to track frequency of (10M).
MaxCost: 1 << 30, // maximum cost of cache (1GB).
BufferItems: 64, // number of keys per Get buffer.
})
......
// set a value with a cost of 1
cache.Set("key", "value", 1)
......
}
其中,有三个关键参数:NumCounters,MaxCost,BufferItems。同时,官方也分别给出了解释:
NumCounters int64
NumCounters is the number of 4-bit access counters to keep for admission and eviction. We've seen good performance in setting this to 10x the number of items you expect to keep in the cache when full.
For example, if you expect each item to have a cost of 1 and MaxCost is 100, set NumCounters to 1,000. Or, if you use variable cost values but expect the cache to hold around 10,000 items when full, set NumCounters to 100,000. The important thing is the number of unique items in the full cache, not necessarily the MaxCost value.
MaxCost int64
MaxCost is how eviction decisions are made. For example, if MaxCost is 100 and a new item with a cost of 1 increases total cache cost to 101, 1 item will be evicted.
MaxCost can also be used to denote the max size in bytes. For example, if MaxCost is 1,000,000 (1MB) and the cache is full with 1,000 1KB items, a new item (that's accepted) would cause 5 1KB items to be evicted.
MaxCost could be anything as long as it matches how you're using the cost values when calling Set.
BufferItems int64
BufferItems is the size of the Get buffers. The best value we've found for this is 64.
If for some reason you see Get performance decreasing with lots of contention (you shouldn't), try increasing this value in increments of 64. This is a fine-tuning mechanism and you probably won't have to touch this.
BufferItem已经说了,最好是设置为64,再根据实际情况微调。但是对于MaxCost 和 NumCounters 如何设置的问题, 什么都说了,又好像什么都没说。
索性大概搜索了一下,发现有人直接翻译为如下的内容:
这真的对吗?NumCouters是指频率?MaxCost是存储容量?带着这些疑问,我翻阅了一下代码,发现并非如此。
MaxCost 的含义,以及作用
先看一下MaxCost 参数,明白了这个之后,NumCounters就很容易了。
Cost在这里可以翻译为“成本”,但是我觉得此时理解为“资源”或者“预算”更合理,MaxCost即最大资源或者最大预算。这个资源可以是key的数量,可以是存储空间,可以是任何维度的概念。只要在Set的时候,cost参数赋予相同的概念就行了。
例子1
假如你计划只放置100个数据在缓存中,即有100个位置用于放置数据;那么你每次set的时候,cost设置为1,即表示你消耗了一个位置。当100个位置消耗完时,就会开始执行淘汰策略。对于这种场景,maxcost的值应设置为100,且在set操作时,cost值为1。
例子2
假如你不关心有多少个数据要缓存,你只是希望划分100k的内存用于缓存数据;同时可能每个数据的大小是不同的,有的占1k,有的占5k,有的占3k; 当剩余内存空间不够用于分配给新数据时,就会执行淘汰策略。对于这种场景,maxcost就是你计划划分的空间,即100;而每次set时的cost参数即数据的大小。
至此,你会明白,Maxcost的含义是资源的最大数量,它的作用是杜绝无限制地使用资源,并及时的触发淘汰策略。maxcost要配合set函数的cost参数使用,使两者具有对等的意义。
NumCounters 的含义,以及作用
上一节提到,当总的资源占用超过maxcost的限制时,会触发淘汰策略,那么如何确定要淘汰哪一个数据呢,根据TinyLFU算法,会尽可能淘汰被访问次数最少的数据,即不经常用的数据,自然就没有缓存的价值。
那么如何确定访问被次数最少的数据呢?显然需要每次访问时,都记录一下。
那么如何记录每一个key的被访问次数呢? 最简单是为每一个数据设置一个计数器(实际情况不是这样,实际用到了Count-Min sketch算法,利用该算法就无需为每一个数据都设置一个计数器,更节省资源,具体的算法内容不是这里要讨论的内容,请自行查阅相关资料)。
总之,我们需要记录每一个数据被访问的次数,即每一个数据都有其对应的计数器。那么NumCounters就是指计数器的数量,即数据的个数,进步一理解为key的数量(所有的key的数量,不仅仅是要缓存的数据的数量)。
那么难题来了,我们怎么知道总共会出现多少个key呢?例如我要以用户名为key,缓存用户信息,那用户名可多了去了,无法估算,怎么办?莫慌,官方预判了你的预判,你不知道总共有多少key,但是你大概可以知道,你计划缓存多少个key吧? 把这个数量乘10 , 就是你NumCounters的数值。
举个例子:我的系统里有10万个用户,但是我计划缓存1000个活跃用户,那么NumCounters为1000 * 10 = 10000。
*另外需要注意,不能简单地认为,NumCounters = MaxCountter 10 ,这只能对应上一节例子1的场景。再举个例子:我计划用1000k的内存空间缓存用户数据(maxcost=1000),但是有的用户信息很完整占用空间很多,假为如20k,有的用户数据很少,只有2k。大概平均计算下来,我只能缓存500个数据,这时NumCounters 就不是1000 * 10 ,而是500 *10。
写在最后:
以上就是我个人对于这两个关键参数的理解,如有问题欢迎交流,谢谢。祝愿各位的程序用上缓存之后,快的飞起!!!