Help us shape the future of Go! Take the 2026 Go Developer Survey

cache

package
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package cache 进程内检查缓存原语(stdlib-only):TTL 缓存 + 刷新进行中 并发合并(inflight singleflight)+ 失败短 TTL。收编自消费方健康检查器的 双份手写形态——/health 轮询会放大昂贵的 refresh(子进程 fan-out/远程探测), 无合并则 N 个观察者并发拉起 N 轮全量刷新。

Package cache 提供进程内缓存原语与泛型缓存门面(stdlib-only,零 oekit 内部依赖)。

组成:

  • Cache[T] / CacheAside[T] / SourceFunc[T] 接口:实现契约统一收拢在 interface.go 的接口 godoc(值语义、(nil, nil) 双关、回填 best-effort、 TTL 来源、生命周期、null marker)
  • LocalCache[T]:基于内存的本地缓存(写序淘汰——FIFO + 重写提升,读 命中不提升;MaxSize、TTL、空值防穿透)
  • SingleFlightCache[T]:装饰器,并发回源合并防击穿(内部 singleflight fork 于 concurrency/singleflight,见 internal/singleflight)
  • Checker:进程内检查缓存原语(TTL 缓存 + 刷新合并 + 失败短 TTL)
  • 缓存键构造:Key / HashKey / UserKey / TenantKey
  • cachetest 子包:CacheAside 实现的契约测试套件

分布式后端 RedisCache[T] 在独立模块 github.com/oekit/redisx(import github.com/oekit/redisx/cache)——本包零 Redis 编译依赖,用 LocalCache 的服务无需引入 Redis SDK。

新增后端(etcd/consul/...)实现 CacheAside[T] 后必须通过 github.com/oekit/pkg/cache/cachetest 契约测试套件(Run 全绿,含 -race) 方可接入。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HashKey added in v0.4.0

func HashKey(prefix string, args ...any) string

HashKey 基于参数生成 hash key,适用于复杂参数的缓存 key。 哈希取 SHA-256 前 16 位 hex(2026-09-15 治理轮:md5 → sha256,缓存 key 属非加密场景但无需为弱哈希留误用空间;摘要值较 v0.28.x 变化,key 为 进程/缓存生命周期内临时值,无持久化契约)。

cache.HashKey("search", "keyword", 1, 20) // "search:a1b2c3d4e5f6g7h8"

func Key added in v0.4.0

func Key(parts ...string) string

Key 用冒号连接各部分生成缓存 key。

cache.Key("user", "123", "profile") // "user:123:profile"

func TenantKey added in v0.4.0

func TenantKey(tenantID, suffix string) string

TenantKey 生成租户维度的缓存 key。

cache.TenantKey("tenant1", "config") // "tenant:tenant1:config"

func UserKey added in v0.4.0

func UserKey(userID int64, suffix string) string

UserKey 生成用户维度的缓存 key。

cache.UserKey(123, "profile") // "user:123:profile"

Types

type BatchGetter added in v0.6.0

type BatchGetter[T any] interface {
	// MGet 批量获取。keys 为空时返回空 map。
	MGet(ctx context.Context, keys ...string) (map[string]*T, error)
}

BatchGetter 是批量读取的可选能力接口:实现方在单次网络往返(或单次 锁区间)内取回一批键,消费方经类型断言探测选用(缓存装饰器应向下 透传:内层具备批量能力时不得在外层退化为逐键)。

实现契约(与包级 (nil, nil) 双关一致的键缺席语义):

  • 返回 map 只含命中真值的键;key 不存在与命中 null marker 一律缺席。 缺席键的消费方应回退 GetOrLoad——真 miss 回源回填,marker 命中在 后端短路,两种情况都不会重复回源;
  • MGet 是纯读:不回源、不回填、不产生 null marker;
  • 批量中的反序列化失败按整批错误处理(与单键 Get 同口径)。

type Cache added in v0.4.0

type Cache[T any] interface {
	// Get 获取缓存。
	// 返回 (nil, nil) 表示数据不存在(见包级"实现契约")。
	Get(ctx context.Context, key string) (*T, error)

	// Set 设置缓存(使用实现构造时的默认 TTL)。
	Set(ctx context.Context, key string, value *T) error

	// SetWithTTL 设置缓存(显式 TTL)。
	SetWithTTL(ctx context.Context, key string, value *T, ttl time.Duration) error

	// Del 删除缓存。key 不存在时不得报错(幂等)。
	Del(ctx context.Context, key string) error

	// DelBatch 批量删除缓存。同 Del 幂等。
	DelBatch(ctx context.Context, keys ...string) error

	// Exists 检查缓存是否存在。
	// 对 null marker 返回 true("缓存知道该 key 不存在"也是一种存在)。
	Exists(ctx context.Context, key string) (bool, error)
}

Cache 是泛型缓存接口,面向接口编程。 只定义通用的缓存操作,不绑定具体实现或模式。

实现契约(所有 Cache[T]/CacheAside[T] 实现必须遵守)

值语义(别名警告):

实现**可以**返回缓存内部共享引用(零拷贝热路径,如 LocalCache),
调用方**禁止改写**返回的 *T 指向的值——改写会直接污染缓存条目,
并与并发读构成 data race;需要可变副本时调用方自行拷贝。
序列化型实现(如 Redis 后端)每次反序列化返回新副本,天然无别名。
同一接口允许两种别名语义,因此调用方一律按"只读"对待返回值。
Set/SetWithTTL 传入的 value 同理:实现不拷贝时调用方此后不得改写。

(nil, nil) 双关:

Get/GetOrLoad 返回 (nil, nil) 表示"数据不存在"——key miss 或命中
null marker(见 SourceFunc)二者之一;error 非 nil 时值无效。
miss 不是 error,不应记错误日志。

回填 best-effort:

GetOrLoad/Refresh 从 Source 取到数据后的回填写入失败**不阻断**返回
已取到的数据(实现自行记日志);下次请求会重新回源。

TTL 来源:

  • Set:使用实现构造时配置的默认 TTL;
  • SetWithTTL:显式传参;
  • GetOrLoad 回填:使用默认 TTL。

TTL<=0 的语义(0 = 不过期还是实现默认值)由各实现的配置文档定义 (LocalCache:0 = 不过期;Redis 后端见各自 Config)。

生命周期:

接口不含 Close——生命周期属于具体实现的资源管理,不由接口约束。
需要停止后台资源的实现自行提供 Close,并遵循仓库硬规则签名
Close(ctx context.Context) error + sync.Once 幂等。

type CacheAside added in v0.4.0

type CacheAside[T any] interface {
	Cache[T]

	// GetOrLoad 获取缓存,未命中时通过 Source 加载并回填。
	// Source 返回 (nil, nil) 时缓存 null marker(防穿透,TTL 见 SourceFunc);
	// 后续调用命中 null marker 直接返回 (nil, nil),不再调用 Source。
	GetOrLoad(ctx context.Context, key string) (*T, error)

	// Refresh 刷新缓存(删除后重新从 Source 加载)。
	Refresh(ctx context.Context, key string) (*T, error)
}

CacheAside 是 Cache-Aside 模式扩展接口。 在 Cache 基础上增加数据源回填、刷新等能力。

GetOrLoad/Refresh 遵循包级"实现契约":miss 非 error、回填 best-effort、 null marker 防穿透。

新增后端(etcd/consul/...)必须实现本接口并通过 github.com/oekit/data/cachetest 契约测试套件(Run 全绿)方可接入。

type Checker

type Checker struct {
	// contains filtered or unexported fields
}

Checker 缓存一次错误型检查的结果:成功缓存 ttl,失败缓存 failTTL(失败多为 瞬时,恢复后不该被旧失败多压一整个正常周期);刷新进行中后来者合并等待同一 次刷新(ctx 感知,不叠加刷新轮次)。并发安全。

func NewChecker

func NewChecker(refresh func(ctx context.Context) error, ttl, failTTL time.Duration, opts ...Option) *Checker

NewChecker 构造;failTTL<=0 时与 ttl 同值。

func (*Checker) Check

func (c *Checker) Check(ctx context.Context) error

Check 返回缓存有效期内的结果;过期则触发一次刷新(并发调用合并为一次)。

type LocalCache added in v0.4.0

type LocalCache[T any] struct {
	// contains filtered or unexported fields
}

LocalCache 是基于内存的本地缓存实现。 适用于单实例场景或作为二级缓存。 淘汰策略为写序淘汰(FIFO + 重写提升,读命中不提升——非严格 LRU): 达到 MaxSize 后淘汰最早写入(且未被重写刷新)的条目。 使用 Close(ctx) 方法停止后台清理 goroutine;Close 可安全重复调用, 签名对齐仓库硬规则 Close(ctx context.Context) error。

func NewLocalCache added in v0.4.0

func NewLocalCache[T any](cfg LocalConfig[T]) *LocalCache[T]

NewLocalCache 创建一个本地缓存实例。

func (*LocalCache[T]) Clear added in v0.4.0

func (c *LocalCache[T]) Clear()

Clear 清空缓存。

func (*LocalCache[T]) Close added in v0.4.0

func (c *LocalCache[T]) Close(ctx context.Context) error

Close 停止后台清理 goroutine(TTL>0 时构造启动的那个)。 sync.Once 保证幂等:重复 Close 不 panic、不返回错误。

ctx 预留:当前实现同步返回 nil,ctx 不阻塞——本实现无外部资源需要 排空,仅关闭内部 done channel 通知 cleanup goroutine 退出。

func (*LocalCache[T]) Del added in v0.4.0

func (c *LocalCache[T]) Del(ctx context.Context, key string) error

Del 删除缓存。

func (*LocalCache[T]) DelBatch added in v0.4.0

func (c *LocalCache[T]) DelBatch(ctx context.Context, keys ...string) error

DelBatch 批量删除缓存。

func (*LocalCache[T]) Exists added in v0.4.0

func (c *LocalCache[T]) Exists(ctx context.Context, key string) (bool, error)

Exists 检查缓存是否存在。

func (*LocalCache[T]) Get added in v0.4.0

func (c *LocalCache[T]) Get(ctx context.Context, key string) (*T, error)

Get 获取缓存。 返回 (nil, nil) 表示数据不存在 —— 包括 key 不存在和命中 null marker 两种情况;GetOrLoad 会区分二者实现防穿透。

⚠️ 返回的是缓存内部的 *T(零拷贝,热路径设计):调用方不得修改指向的 值——会污染缓存并与并发读形成 data race。需要可变副本时自行拷贝。 (RedisCache 每次反序列化返回新副本,两实现语义差异见各自身文档。)

func (*LocalCache[T]) GetOrLoad added in v0.4.0

func (c *LocalCache[T]) GetOrLoad(ctx context.Context, key string) (*T, error)

GetOrLoad 获取缓存,未命中时通过 Source 加载(Cache-Aside 模式)。 Source 返回 (nil, nil) 时缓存 null marker(默认 1 分钟,可用 NullTTL 调整,负值禁用);命中 null marker 时直接返回 (nil, nil),**不会**再调 Source,避免对不存在的 key 形成 thundering-herd 击穿。

func (*LocalCache[T]) MGet added in v0.6.0

func (c *LocalCache[T]) MGet(ctx context.Context, keys ...string) (map[string]*T, error)

MGet 批量获取:单次读锁区间取回一批键(契约见 BatchGetter——缺席语义 与 (nil, nil) 双关一致:不存在与 null marker 一律缺席;纯读不回填, 已过期条目按缺席处理,回收留给 Get/Exists 的双检路径——TTL=0 时无 cleanup goroutine,过期条目仅在这些路径被惰性回收)。

func (*LocalCache[T]) Refresh added in v0.4.0

func (c *LocalCache[T]) Refresh(ctx context.Context, key string) (*T, error)

Refresh 刷新缓存(删除后重新从 Source 加载)。

func (*LocalCache[T]) Set added in v0.4.0

func (c *LocalCache[T]) Set(ctx context.Context, key string, value *T) error

Set 设置缓存(使用默认 TTL)。

func (*LocalCache[T]) SetWithTTL added in v0.4.0

func (c *LocalCache[T]) SetWithTTL(ctx context.Context, key string, value *T, ttl time.Duration) error

SetWithTTL 设置缓存(自定义 TTL)。

func (*LocalCache[T]) Size added in v0.4.0

func (c *LocalCache[T]) Size() int

Size 返回当前缓存条目数。

type LocalConfig added in v0.4.0

type LocalConfig[T any] struct {
	// TTL 缓存过期时间。0 表示不过期。
	TTL time.Duration

	// MaxSize 最大缓存条目数。0 表示不限制。
	MaxSize int

	// Source 缓存未命中时的数据获取函数(Cache-Aside 模式)。
	Source SourceFunc[T]

	// NullTTL 空值缓存过期时间(防穿透)。
	//   - 0(未设置):使用默认值 1 分钟;
	//   - 负值:显式禁用空值缓存(Source 返回 nil 时不写 null marker,
	//     下次 GetOrLoad 会重新调用 Source);
	//   - 正值:按指定时长缓存 null marker。
	NullTTL time.Duration
}

LocalConfig 配置本地缓存实例。

type Option

type Option func(*Checker)

Option Checker 可选项。

func WithClock

func WithClock(now func() time.Time) Option

WithClock 注入时钟(测试用;缺省 time.Now)。

type SingleFlightCache added in v0.4.0

type SingleFlightCache[T any] struct {
	// contains filtered or unexported fields
}

SingleFlightCache 是带 singleflight 保护的缓存装饰器。 同一个 key 的并发请求只会有一个实际调用 Source,其他请求等待共享结果。 防止缓存击穿(热点 key 过期时的雷群效应)。

可以包装任何 CacheAside[T] 实现(Redis、本地缓存等)。

func NewSingleFlightCache added in v0.4.0

func NewSingleFlightCache[T any](inner CacheAside[T]) *SingleFlightCache[T]

NewSingleFlightCache 创建带 singleflight 保护的缓存装饰器。 inner 可以是 RedisCache、LocalCache 或任何实现 CacheAside[T] 的缓存。

func (*SingleFlightCache[T]) Del added in v0.4.0

func (sf *SingleFlightCache[T]) Del(ctx context.Context, key string) error

Del 删除缓存。

func (*SingleFlightCache[T]) DelBatch added in v0.4.0

func (sf *SingleFlightCache[T]) DelBatch(ctx context.Context, keys ...string) error

DelBatch 批量删除缓存。

func (*SingleFlightCache[T]) Exists added in v0.4.0

func (sf *SingleFlightCache[T]) Exists(ctx context.Context, key string) (bool, error)

Exists 检查缓存是否存在。

func (*SingleFlightCache[T]) Get added in v0.4.0

func (sf *SingleFlightCache[T]) Get(ctx context.Context, key string) (*T, error)

Get 获取缓存(纯查询,不触发 Source)。

func (*SingleFlightCache[T]) GetOrLoad added in v0.4.0

func (sf *SingleFlightCache[T]) GetOrLoad(ctx context.Context, key string) (*T, error)

GetOrLoad 获取缓存,带 singleflight 保护。 同一个 key 的并发请求只会有一个实际调用 Source。

流程:

  1. 查缓存,命中则直接返回
  2. 未命中时通过 singleflight 去重,只有一个 goroutine 调用 Source
  3. Source 返回后回填缓存
  4. 其他 goroutine 共享同一份结果

func (*SingleFlightCache[T]) MGet added in v0.6.0

func (sf *SingleFlightCache[T]) MGet(ctx context.Context, keys ...string) (map[string]*T, error)

MGet 批量获取:内层具备批量能力时直接委托(装饰器不得退化批量); 内层无批量能力时退化为逐键 Get。

func (*SingleFlightCache[T]) Refresh added in v0.4.0

func (sf *SingleFlightCache[T]) Refresh(ctx context.Context, key string) (*T, error)

Refresh 刷新缓存(带 singleflight 保护)。

func (*SingleFlightCache[T]) Set added in v0.4.0

func (sf *SingleFlightCache[T]) Set(ctx context.Context, key string, value *T) error

Set 设置缓存。

func (*SingleFlightCache[T]) SetWithTTL added in v0.4.0

func (sf *SingleFlightCache[T]) SetWithTTL(ctx context.Context, key string, value *T, ttl time.Duration) error

SetWithTTL 设置缓存(自定义 TTL)。

type SourceFunc added in v0.4.0

type SourceFunc[T any] func(ctx context.Context, key string) (*T, error)

SourceFunc 缓存未命中时的数据获取函数。

返回 (nil, nil) 表示数据不存在——实现会缓存 null marker 防穿透: 后续 GetOrLoad 命中 null marker 时直接返回 (nil, nil),不再调用 Source; Exists 对 null marker 返回 true。

null 标记的 TTL 语义由各实现的配置定义 (LocalCache:NullTTL 0 = 默认 1 分钟,负值 = 显式禁用空值缓存)。

Source 返回的 *T 所有权随值语义契约:实现回填后调用方与实现共享引用 或各持副本,取决于实现(同"实现契约"的值语义,调用方一律只读对待)。

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL