BigCache 入门教程

快速、并发、可驱逐的内存缓存,在不影响性能的情况下,可以保存大量条目。BigCache 将条目保存在堆上,但省略对它们的 GC。为此,需要对字节片进行操作,因此在大多数用例中需要在缓存前对条目进行(反)序列化。

需要 Go 1.12 或更新版本。


1. 使用

1.1. 简单初始化

import (
	"fmt"
	"context"
	"github.com/allegro/bigcache/v3"
)

cache, _ := bigcache.New(context.Background(), bigcache.DefaultConfig(10 * time.Minute))

cache.Set("my-unique-key", []byte("value"))

entry, _ := cache.Get("my-unique-key")
fmt.Println(string(entry))

1.2. 自定义初始化

当可以提前预测缓存负载时,最好使用自定义初始化,因为这样可以避免额外的内存分配。

import (
	"log"

	"github.com/allegro/bigcache/v3"
)

config := bigcache.Config {
		// 缓存分片的数量,必须是 2 的幂
		Shards: 1024,

		// 在该时间后,条目将被踢除
		LifeWindow: 10 * time.Minute,

		// 两次移除过期条目(清理)之间的时间间隔。
		// 如果设置为 <= 0 的值,那么不执行任务操作。
		// 设置为 < 1 秒的值适得其反 - bigcache 有 1 秒的分辨能力
		CleanWindow: 5 * time.Minute,

		// 窗口中的最大条目数。仅用于为缓存分片计算初始大小。
		// 如果正确设置,那么不会发生额外的内存分配
		MaxEntriesInWindow: 1000 * 10 * 60,

		// 条目的最大大小,单位是字节。仅用于为缓存分片计算初始大小
		MaxEntrySize: 500,

		// Verbose 模式打印有关新内存分配的信息
		Verbose: true,

		// HardMaxCacheSize 是 BytesQueue 大小的限制,单位是 MB。
		// 它可以防止应用程序消耗机器上的所有可用内存,从而避免 OOM Killer。
    // 默认值为 0,表示不限制大小。当该限制大于 0,并且达到时,
    // 新条目将覆盖旧条目。由于分片的额外内存,最大内存消耗将大于 HardMaxCacheSize。
    // 每个分片为键映射和统计信息(map[uint64]uint32)消耗额外的内存。
    // 该 map 的大小等于缓存中的条目数 ~ 2×(64+32)×n 位 + 开销或者 map 本身
		HardMaxCacheSize: 8192,

		// 当最旧的条目被移除时,将触发该回调函数。条目被删除的原因包括:
		// 1. 其过期时间;
		// 2. 没有新空间留给新条目
		// 3. 调用 delete
		OnRemove: nil,

		// 与 OnRemove 相比,会传入代表原因的常量
		OnRemoveWithReason: nil,
	}

cache, initErr := bigcache.New(context.Background(), config)
if initErr != nil {
	log.Fatal(initErr)
}

cache.Set("my-unique-key", []byte("value"))

if entry, err := cache.Get("my-unique-key"); err == nil {
	fmt.Println(string(entry))
}

1.3 LifeWindowCleanWindow

  1. LifeWindow 是一个时间。在此时间之后,条目成为失效条目,但未被删除。
  1. CleanWindow 是一个时间。在此时间之后,将删除所有失效条目,但不删除仍然有效的条目。

2. 工作原理

BigCache 依赖 Go 1.15 版本中出现的优化(issue-9477)。该优化表明如果使用键和值中没有指针的 map,那么 GC 将忽略其内容。因此,BigCache 使用 map[uint64]uint32,其中键是哈希值,值是条目的偏移量。

条目保存在字节片中,以省略 GC。字节片的大小可以增长到千兆字节,而不影响性能,因为 GC 只能看到指向它的单个指针。

2.1. 冲突

BigCache 不处理冲突。插入新条目时,如果其哈希值与之前存储的条目冲突,新条目将重写之前存储的值。