cache

package
v0.0.15 Latest Latest
Warning

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

Go to latest
Published: Nov 20, 2025 License: MIT Imports: 3 Imported by: 0

README

cache

简介

cache 包提供了一个统一的缓存接口和多种缓存实现,适用于需要高性能内存缓存的 Go 应用程序。默认使用 Ristretto 作为底层实现,提供高效、可靠的缓存功能。

主要特性
  • 支持多种缓存后端(默认使用 ristretto)
  • 提供统一的缓存接口
  • 支持泛型和类型安全
  • 支持 TTL(生存时间)设置
  • 支持全局缓存实例
  • 线程安全
  • 高并发性能
设计理念

cache 包的设计遵循以下原则:

  1. 简单性:提供简洁易用的 API,使缓存操作尽可能直观
  2. 灵活性:支持多种缓存用例,从简单到复杂
  3. 类型安全:通过泛型支持类型安全的缓存操作,减少运行时错误
  4. 可扩展性:通过统一接口支持不同的缓存后端
  5. 性能优先:所有实现都注重性能和效率

包内部使用了适配器模式将不同的缓存后端实现适配到统一的 Cache 接口,同时提供了函数选项模式进行灵活配置。此外,利用泛型特性提供了类型安全的缓存操作,可以避免手动类型断言的麻烦。

安装

前置条件
  • Go 版本要求:Go 1.18+ (支持泛型)
  • 依赖要求:
    • github.com/dgraph-io/ristretto v0.2.0
安装命令
go get -u github.com/fsyyft-go/kit/cache

快速开始

基础用法
// 创建缓存实例
cache, err := cache.NewCache()  // 使用默认配置
if err != nil {
    panic(err)
}
defer cache.Close()

// 基本操作
cache.Set("key", "value")                    // 设置永不过期的值
cache.SetWithTTL("temp", "value", time.Hour) // 设置 1 小时后过期的值

// 获取值
if val, exists := cache.Get("key"); exists {
    fmt.Println(val)
}

// 获取带 TTL 的值
if val, exists, ttl := cache.GetWithTTL("temp"); exists {
    fmt.Printf("值:%v,剩余时间:%v\n", val, ttl)
}
配置选项
// 使用自定义配置
cache, err := cache.NewCache(
    cache.WithNumCounters(1e7),  // 跟踪 1000 万个条目
    cache.WithMaxCost(1<<30),    // 最大内存使用 1GB
    cache.WithBufferItems(64),   // 默认缓冲区大小
)
if err != nil {
    panic(err)
}
defer cache.Close()

详细指南

核心概念
  1. 缓存接口Cache 接口定义了所有缓存实现必须提供的基本操作
  2. 过期时间:通过 TTL (Time-To-Live) 控制缓存项的生存时间
  3. 驱逐策略:当缓存达到容量限制时,最不经常使用的项目将被自动移除
  4. 类型安全TypedCache[T] 提供类型安全的缓存操作,避免类型断言
  5. 全局缓存:通过全局函数可以方便地访问共享的缓存实例
常见用例
1. 使用全局缓存
// 初始化全局缓存
if err := cache.InitCache(); err != nil {
    panic(err)
}
defer cache.Close()

// 使用全局缓存函数
cache.Set("key", "value")
if val, exists := cache.Get("key"); exists {
    fmt.Println(val)
}

// 清理全局缓存
cache.Clear()
2. 类型安全的缓存操作
// 定义结构体
type User struct {
    ID   int
    Name string
    Age  int
}

// 创建缓存实例
baseCache, _ := cache.NewCache()
userCache := cache.AsTypedCache[User](baseCache)

// 存储用户对象
user := User{ID: 1, Name: "张三", Age: 30}
userCache.Set("user:1", user)

// 获取用户对象(无需类型断言)
if user, exists := userCache.Get("user:1"); exists {
    fmt.Printf("找到用户: %s, 年龄: %d\n", user.Name, user.Age)
}
最佳实践
  • 合理设置配置参数

    • NumCounters 建议设置为预期条目数的 10 倍
    • MaxCost 根据实际内存限制设置
    • BufferItems 默认值 64 适合大多数场景
  • 使用类型安全的接口

    • 优先使用 TypedCache 避免类型断言
    • 为不同类型的数据创建专门的缓存实例
  • 性能优化

    • 合理设置缓存大小避免频繁驱逐
    • 注意及时清理不需要的缓存项
    • 使用 Close 释放资源
  • 错误处理

    • 总是检查 InitCache 的返回错误
    • 使用 defer Close() 确保资源释放
    • 检查 Get 操作的 exists 返回值

API 文档

主要类型
// Cache 定义了统一的缓存接口
type Cache interface {
    Get(key interface{}) (value interface{}, exists bool)
    GetWithTTL(key interface{}) (value interface{}, exists bool, remainingTTL time.Duration)
    Set(key interface{}, value interface{}) bool
    SetWithTTL(key interface{}, value interface{}, ttl time.Duration) bool
    Delete(key interface{})
    Clear()
    Close() error
}

// TypedCache 是一个泛型包装器,提供类型安全的缓存操作
type TypedCache[T any] struct {
    // 内部字段
}

// CacheOptions 定义了缓存的配置选项
type CacheOptions struct {
    NumCounters int64
    MaxCost     int64
    BufferItems int64
}
关键函数
NewCache

创建一个新的缓存实例,默认使用 Ristretto 实现。

func NewCache(options ...Option) (Cache, error)

示例:

cache, err := cache.NewCache(
    cache.WithNumCounters(1e7),
    cache.WithMaxCost(1<<30),
)
InitCache

初始化全局缓存实例,确保只被初始化一次。

func InitCache(options ...Option) error

示例:

if err := cache.InitCache(); err != nil {
    panic(err)
}
AsTypedCache

将缓存转换为类型安全的包装器。

func AsTypedCache[T any](cache Cache) *TypedCache[T]

示例:

strCache := cache.AsTypedCache[string](baseCache)
错误处理

cache 包的大多数操作都会返回一个 bool 值表示操作是否成功,但创建和初始化函数会返回详细的错误信息。建议始终检查这些错误:

cache, err := cache.NewCache()
if err != nil {
    // 处理错误:日志记录、返回错误或中止
    log.Fatalf("创建缓存失败: %v", err)
}

对于获取操作,应始终检查 exists 返回值:

if value, exists := cache.Get(key); exists {
    // 使用 value
} else {
    // 处理键不存在的情况
}

性能指标

基于最近的基准测试结果:

操作 性能指标 说明
Set ~1930 ns/op, 442 B/op, 6 allocs/op 设置缓存项的基本性能
Get ~46.43 ns/op, 17 B/op, 1 allocs/op 获取缓存项的基本性能,非常快
SetWithTTL ~2414 ns/op, 674 B/op, 6 allocs/op 设置带TTL的性能,稍慢于基本Set
GetWithTTL ~278.1 ns/op, 22 B/op, 1 allocs/op 获取带TTL的性能,慢于基本Get
TypedCache Set ~1947 ns/op, 615 B/op, 7 allocs/op 类型安全的Set操作
TypedCache Get ~62.22 ns/op, 18 B/op, 1 allocs/op 类型安全的Get操作
Global Cache Set ~78.10 ns/op, 40 B/op, 2 allocs/op 全局缓存Set性能
Global Cache Get ~14.66 ns/op, 16 B/op, 1 allocs/op 全局缓存Get性能,非常快

测试覆盖率

覆盖率
github.com/fsyyft-go/kit/cache 71.1%

调试指南

日志级别

cache 包本身不输出日志,建议在应用程序中集成适当的日志记录:

  • 记录缓存初始化和配置
  • 记录缓存相关的重要操作
  • 监控缓存命中率和性能指标
常见问题排查
内存使用过高

确保设置了合理的 MaxCost 值,这会限制缓存使用的最大内存。

项目过早被驱逐

如果缓存项被过早驱逐,考虑增加 NumCounters 和 MaxCost 值。NumCounters 应该是预期独特项数的约 10 倍。

性能问题

如果遇到性能瓶颈,请确保:

  • 缓存配置适合您的使用场景
  • 避免在热路径上频繁调用 SetWithTTL
  • 考虑使用批量操作而不是多次单个操作

相关文档

贡献指南

我们欢迎任何形式的贡献,包括但不限于:

  • 报告问题
  • 提交功能建议
  • 提交代码改进
  • 完善文档

请参考我们的贡献指南了解详细信息。

许可证

本项目采用 MIT License 许可证。查看 LICENSE 文件了解更多信息。

Documentation

Overview

Package cache 提供了一个统一的缓存接口和多种缓存实现。

主要特性:

  • 支持多种缓存后端(默认使用 Ristretto)
  • 提供统一的缓存接口
  • 支持泛型和类型安全
  • 支持 TTL(生存时间)设置
  • 支持全局缓存实例
  • 线程安全
  • 高性能并发操作
  • 内存使用优化

配置选项:

  • NumCounters:缓存跟踪的最大条目数(建议为预期条目数的 10 倍)
  • MaxCost:缓存的最大成本(可理解为最大条目数或最大内存使用量)
  • BufferItems:写入操作的缓冲大小(影响并发性能)

基本使用:

// 创建缓存实例
cache, err := cache.NewCache()  // 使用默认配置
if err != nil {
    panic(err)
}
defer cache.Close()

// 基本操作
cache.Set("key", "value")                    // 设置永不过期的值
cache.SetWithTTL("temp", "value", time.Hour) // 设置 1 小时后过期的值

// 获取值
if val, exists := cache.Get("key"); exists {
    fmt.Println(val)
}

// 获取带 TTL 的值
if val, exists, ttl := cache.GetWithTTL("temp"); exists {
    fmt.Printf("值:%v,剩余时间:%v\n", val, ttl)
}

类型安全的缓存使用:

// 创建类型安全的缓存包装器
userCache := cache.AsTypedCache[User](cache)

// 存储特定类型的值
user := User{ID: 1, Name: "admin"}
userCache.Set("user:1", user)

// 获取特定类型的值(无需类型断言)
if user, exists := userCache.Get("user:1"); exists {
    fmt.Printf("用户名:%s\n", user.Name)
}

自定义配置:

// 使用自定义配置创建缓存
cache, err := cache.NewCache(
    cache.WithNumCounters(1e7),    // 1000万个计数器
    cache.WithMaxCost(1<<30),      // 1GB 最大内存
    cache.WithBufferItems(64),      // 64 个缓冲项
)
if err != nil {
    panic(err)
}
defer cache.Close()

性能优化建议:

1. NumCounters 设置:

  • 设置为预期独特条目数的 10 倍
  • 例如:预期 100 万个键,设置为 1000 万

2. MaxCost 设置:

  • 根据可用内存和数据大小设置
  • 可以使用字节数或条目数作为度量

3. BufferItems 设置:

  • 影响写入性能和内存使用
  • 默认值 64 适合大多数场景
  • 增加可提高并发性能,但会使用更多内存

注意事项:

1. 在程序退出前调用 Close() 释放资源 2. 合理设置 TTL 避免内存泄漏 3. 注意类型安全包装器的使用场景 4. 监控缓存命中率和内存使用情况

更多示例请参考 example/cache 目录。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Clear

func Clear()

Clear 清空全局缓存。 如果全局缓存未初始化,该操作将被忽略。 这个操作会立即使所有缓存项失效。

func Close

func Close() error

Close 关闭全局缓存。 如果全局缓存未初始化,将返回 nil。 关闭后的全局缓存不应该再被使用。

返回值:

  • error:如果关闭过程中发生错误则返回错误。

func Delete

func Delete(key interface{})

Delete 从全局缓存中删除值。 如果全局缓存未初始化,该操作将被忽略。

参数:

  • key:要删除的缓存键,可以是任意类型。

func Get

func Get(key interface{}) (interface{}, bool)

Get 从全局缓存中获取值。 如果全局缓存未初始化,将返回 (nil, false)。

参数:

  • key:要获取的缓存键,可以是任意类型。

返回值:

  • value:缓存的值,如果不存在则为 nil。
  • exists:值是否存在且未过期。

func GetWithTTL

func GetWithTTL(key interface{}) (interface{}, bool, time.Duration)

GetWithTTL 从全局缓存中获取值及其剩余过期时间。 如果全局缓存未初始化,将返回 (nil, false, 0)。

参数:

  • key:要获取的缓存键,可以是任意类型。

返回值:

  • value:缓存的值,如果不存在则为 nil。
  • exists:值是否存在且未过期。
  • remainingTTL:剩余过期时间:
  • 如果值不存在或已过期,返回 0。
  • 如果值永不过期,返回 -1。
  • 否则返回实际的剩余时间。

func InitCache

func InitCache(options ...Option) error

InitCache 初始化全局缓存实例。 这个函数使用 sync.Once 确保全局缓存只被初始化一次。 如果已经初始化过,该函数将不会执行任何操作。

参数:

  • options:可选的配置选项,如果不提供则使用默认配置。

返回值:

  • error:如果初始化失败则返回错误。

示例:

// 使用默认配置
if err := cache.InitCache(); err != nil {
    panic(err)
}
defer cache.Close()

// 使用自定义配置
if err := cache.InitCache(
    cache.WithNumCounters(1e7),
    cache.WithMaxCost(1<<30),
    cache.WithBufferItems(64),
); err != nil {
    panic(err)
}
defer cache.Close()

// 使用 CacheOptions 配置(迁移方式)
if err := cache.InitCache(cache.WithOptions(cache.DefaultConfig())...); err != nil {
    panic(err)
}
defer cache.Close()

func Set

func Set(key interface{}, value interface{}) bool

Set 设置全局缓存中的值。 如果全局缓存未初始化,将返回 false。

参数:

  • key:缓存键,可以是任意类型。
  • value:要缓存的值,可以是任意类型。

返回值:

  • bool:是否设置成功。

func SetWithTTL

func SetWithTTL(key interface{}, value interface{}, ttl time.Duration) bool

SetWithTTL 设置全局缓存中的值,带过期时间。 如果全局缓存未初始化,将返回 false。

参数:

  • key:缓存键,可以是任意类型。
  • value:要缓存的值,可以是任意类型。
  • ttl:过期时间,如果 <= 0 则表示永不过期。

返回值:

  • bool:是否设置成功。

Types

type Cache

type Cache interface {
	// Get 获取缓存中的值。
	// 如果键不存在或已过期,exists 将返回 false。
	// 参数:
	//   - key:要获取的缓存键,可以是任意类型。
	// 返回值:
	//   - value:缓存的值,如果不存在则为 nil。
	//   - exists:值是否存在且未过期。
	Get(key interface{}) (value interface{}, exists bool)

	// GetWithTTL 获取缓存中的值及其剩余过期时间。
	// 如果键不存在或已过期,exists 将返回 false。
	// 参数:
	//   - key:要获取的缓存键,可以是任意类型。
	// 返回值:
	//   - value:缓存的值,如果不存在则为 nil。
	//   - exists:值是否存在且未过期。
	//   - remainingTTL:剩余过期时间:
	//     * 如果值不存在或已过期,返回 0。
	//     * 如果值永不过期,返回 -1。
	//     * 否则返回实际的剩余时间。
	GetWithTTL(key interface{}) (value interface{}, exists bool, remainingTTL time.Duration)

	// Set 设置缓存值,该值永不过期。
	// 参数:
	//   - key:缓存键,可以是任意类型。
	//   - value:要缓存的值,可以是任意类型。
	// 返回值:
	//   - bool:是否设置成功。
	Set(key interface{}, value interface{}) bool

	// SetWithTTL 设置带过期时间的缓存值。
	// 参数:
	//   - key:缓存键,可以是任意类型。
	//   - value:要缓存的值,可以是任意类型。
	//   - ttl:过期时间,如果 <= 0 则表示永不过期。
	// 返回值:
	//   - bool:是否设置成功。
	SetWithTTL(key interface{}, value interface{}, ttl time.Duration) bool

	// Delete 从缓存中删除指定的键。
	// 如果键不存在,该操作也会成功返回。
	// 删除后,该键的所有后续访问都将返回不存在。
	// 参数:
	//   - key:要删除的缓存键,可以是任意类型。
	Delete(key interface{})

	// Clear 清空缓存中的所有内容。
	// 这个操作会立即使所有缓存项失效。
	// 在清空后,所有之前的键都将返回不存在。
	Clear()

	// Close 关闭缓存,释放相关资源。
	// 关闭后的缓存不应该再被使用。
	// 重复调用 Close 是安全的。
	// 返回值:
	//   - error:如果关闭过程中发生错误则返回错误。
	Close() error
}

Cache 定义了统一的缓存接口。 这个接口提供了基本的缓存操作功能,可以通过不同的实现来支持不同的缓存后端。 所有的实现都必须保证线程安全。

func NewCache

func NewCache(options ...Option) (Cache, error)

NewCache 创建一个新的缓存实例。 使用 Option 模式配置缓存参数,如果没有提供任何选项,将使用默认配置:

  • NumCounters:1000 万(适合跟踪 100 万个不同的键)
  • MaxCost:1GB(适合存储较大的数据集)
  • BufferItems:64(提供良好的并发性能)

参数:

  • options:可选的配置选项,如果不提供则使用默认配置。

返回值:

  • Cache:缓存接口实现。
  • error:如果创建失败则返回错误。

示例:

// 使用默认配置
cache, err := cache.NewCache()
if err != nil {
    panic(err)
}
defer cache.Close()

// 使用自定义配置
cache, err := cache.NewCache(
    cache.WithNumCounters(1e7),
    cache.WithMaxCost(1<<30),
    cache.WithBufferItems(64),
)
if err != nil {
    panic(err)
}
defer cache.Close()

type CacheOptions

type CacheOptions struct {
	// NumCounters 定义了缓存跟踪的最大条目数。
	// 这个数字应该是预期独特条目数的大约 10 倍。
	// 例如,如果预计会有 1 万个不同的键,则应设置为 10 万。
	NumCounters int64

	// MaxCost 定义了缓存的最大成本。
	// 对于简单的缓存使用场景,这可以理解为最大条目数。
	// 当缓存达到这个限制时,最少使用的项目将被驱逐。
	MaxCost int64

	// BufferItems 定义了在写入操作时的缓冲大小。
	// 更大的缓冲区会导致更好的并发性能,但会使用更多的内存。
	// 对于大多数场景,默认值 64 是合适的。
	BufferItems int64
}

CacheOptions 定义了缓存的配置选项。 这些配置项会影响缓存的性能和资源使用。

type Option

type Option func(*CacheOptions)

Option 定义了缓存配置的函数选项。 这是一种函数式选项模式,用于灵活配置缓存参数。

func WithBufferItems

func WithBufferItems(bufferItems int64) Option

WithBufferItems 设置写入操作时的缓冲大小。 更大的缓冲区会导致更好的并发性能,但会使用更多的内存。 参数:

  • bufferItems:要设置的缓冲区大小。

返回值:

  • Option:用于配置缓存的函数选项。

func WithMaxCost

func WithMaxCost(maxCost int64) Option

WithMaxCost 设置缓存的最大成本。 对于简单的缓存使用场景,这可以理解为最大条目数。 参数:

  • maxCost:要设置的最大成本。

返回值:

  • Option:用于配置缓存的函数选项。

func WithNumCounters

func WithNumCounters(numCounters int64) Option

WithNumCounters 设置缓存跟踪的最大条目数。 numCounters 应该是预期独特条目数的大约 10 倍。 参数:

  • numCounters:要设置的计数器数量。

返回值:

  • Option:用于配置缓存的函数选项。

type TypedCache

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

TypedCache 是一个泛型包装器,提供类型安全的缓存操作。 通过泛型参数 T 指定缓存值的类型,避免了手动类型断言。 这个包装器适用于需要类型安全的场景,例如:

  • 存储特定类型的数据
  • 避免运行时类型错误
  • 提供更好的 IDE 支持

func AsTypedCache

func AsTypedCache[T any](cache Cache) *TypedCache[T]

AsTypedCache 将缓存转换为类型安全的包装器。 参数:

  • cache:底层的缓存实现。

返回值:

  • *TypedCache[T]:类型安全的缓存包装器。

示例:

baseCache := cache.NewCache()
strCache := cache.AsTypedCache[string](baseCache)
intCache := cache.AsTypedCache[int](baseCache)

func (*TypedCache[T]) Clear

func (tc *TypedCache[T]) Clear()

Clear 清空缓存中的所有内容。 这个方法会立即使所有缓存项失效。

func (*TypedCache[T]) Close

func (tc *TypedCache[T]) Close() error

Close 关闭缓存,释放相关资源。 这个方法会关闭底层的缓存实现。 返回值:

  • error:如果关闭过程中发生错误则返回错误。

func (*TypedCache[T]) Delete

func (tc *TypedCache[T]) Delete(key interface{})

Delete 从缓存中删除指定的键。 这个方法会立即使指定的键失效。 参数:

  • key:要删除的缓存键,可以是任意类型。

func (*TypedCache[T]) Get

func (tc *TypedCache[T]) Get(key interface{}) (value T, exists bool)

Get 获取缓存中的值,并进行类型转换。 如果键不存在、已过期或类型不匹配,exists 将返回 false。 返回的值已经是正确的类型,无需进行类型断言。 参数:

  • key:要获取的缓存键,可以是任意类型。

返回值:

  • value:缓存的值,已转换为类型 T。
  • exists:值是否存在且未过期且类型匹配。

func (*TypedCache[T]) GetWithTTL

func (tc *TypedCache[T]) GetWithTTL(key interface{}) (value T, exists bool, remainingTTL time.Duration)

GetWithTTL 获取缓存中的值及其剩余过期时间,并进行类型转换。 如果键不存在、已过期或类型不匹配,exists 将返回 false。 返回的值已经是正确的类型,无需进行类型断言。 参数:

  • key:要获取的缓存键,可以是任意类型。

返回值:

  • value:缓存的值,已转换为类型 T。
  • exists:值是否存在且未过期且类型匹配。
  • remainingTTL:剩余过期时间:
  • 如果值不存在或已过期,返回 0。
  • 如果值永不过期,返回 -1。
  • 否则返回实际的剩余时间。

func (*TypedCache[T]) Set

func (tc *TypedCache[T]) Set(key interface{}, value T) bool

Set 设置缓存中的值。 这个方法会将值保存在缓存中,永不过期。 参数:

  • key:缓存键,可以是任意类型。
  • value:要缓存的值,类型为 T。

返回值:

  • bool:是否设置成功。

func (*TypedCache[T]) SetWithTTL

func (tc *TypedCache[T]) SetWithTTL(key interface{}, value T, ttl time.Duration) bool

SetWithTTL 设置缓存中的值,带过期时间。 这个方法会将值保存在缓存中,在指定的过期时间后自动失效。 参数:

  • key:缓存键,可以是任意类型。
  • value:要缓存的值,类型为 T。
  • ttl:过期时间,如果 <= 0 则表示永不过期。

返回值:

  • bool:是否设置成功。

Jump to

Keyboard shortcuts

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