在GoFrame中优雅地使用gcache

325 阅读5分钟

GoFrame是一个功能强大、可扩展的Go语言Web服务器开发框架。框架提供了一个非常实用的gcache模块,方便我们在项目中实现高性能的缓存功能。本文将详细介绍如何在GoFrame项目里优雅地使用gcache。

gcache简介

gcache是GoFrame框架提供的一个通用的K-V内存缓存模块。它提供了较为完善的缓存功能,包括缓存过期淘汰、缓存命中统计、缓存分组等实用特性。gcache的API设计简洁明了,学习曲线平缓,上手非常容易。

gcache的特点包括:

  • 并发安全:gcache的所有操作都是并发安全的,不必额外加锁
  • 内存限制:可以给缓存设置使用的最大内存,防止无限增长
  • 过期淘汰:支持缓存Key的过期时间,定期自动淘汰
  • LRU淘汰:在内存超限时会自动淘汰最近最少使用的Key
  • 命中统计:提供缓存命中次数和未命中次数的统计
  • 分组隔离:支持按分组隔离缓存,避免Key冲突

基本使用

在GoFrame项目里,可以通过gcache.New()方法非常方便地创建和使用缓存对象。例如:

import (
    "github.com/gogf/gf/v2/os/gctx"
    "github.com/gogf/gf/os/gcache"
    "github.com/gogf/gf/v2/frame/g"
)

func main() {
    ctx := gctx.New()
    c := gcache.New()

    // 设置缓存
    c.Set(ctx, "k1", "v1", 0)

    // 获取缓存,如果key不存在则返回nil
    v, err := c.Get(ctx, "k1")
    if err != nil {
       g.Log().Error(ctx, err)
       return
    }
    g.Dump(v)

    // 获取缓存,如果key不存在则返回指定的默认值
    v, err = c.GetOrSet(ctx, "k2", "v2", 0)
    g.Dump(v)

    if err != nil {
       g.Log().Error(ctx, err)
       return
    }

    // 删除缓存
    c.Remove(ctx, "k2")
}

其中c.Set用于设置一个缓存,c.Get用于获取一个缓存,c.GetOrSet则在缓存不存在时自动设置并返回默认值。

过期淘汰

可以给缓存Key设置一个过期时间,单位为秒。例如:

c.Set(ctx, "k1", "v1", 10*time.Second) // 设置k1这个key 10秒后过期删除

gcache会启动一个定期检查的任务,自动删除到期的Key。我们也可以通过c.UpdateExpire来给已存在的一个Key设置新的过期时间。

LRU淘汰

当缓存的内存占用达到设置的上限时,gcache会自动淘汰掉最近最少访问的Key,以便给新的缓存腾出空间。

我们在创建缓存对象时,可以通过gcache.New方法设置缓存的最大元素个数。gcache会在内部自动管理缓存的内存占用。

缓存分组

在一些复杂的项目中,可能需要按业务、功能模块划分多个缓存,避免不同业务间出现Key冲突。gcache支持按分组隔离缓存。不同分组的缓存对象是相互隔离的。

// 创建一个缓存分组
uc := gcache.New()

// 创建另一个缓存分组
oc := gcache.New()

// 在1分组里缓存数据
uc.Set(ctx, "k1", "v1", 0)

// 在2分组里缓存数据
oc.Set(ctx, "k1", "v2", 0)

g.Dump(uc.Get(ctx, "k1")) // "v1"
g.Dump(oc.Get(ctx, "k1")) // "v2"

Redis适配器

gcache提供了AdapterRedis适配器,可以方便地将数据缓存到Redis中。使用Redis适配器可以将缓存数据持久化,并且可以在多个服务节点间共享缓存。

cache := gcache.New()
// 创建Redis适配器
redisDb, err := gredis.New(redisConfig)
adapter := gcache.NewAdapterRedis(redisDb)

// 创建缓存对象,指定使用Redis适配器
cache.SetAdapter(adapter)

// 使用缓存
cache.Set(ctx, "k1", "v1", 0)
g.Dump(cache.Get(ctx, "k1"))

在创建AdapterRedis适配器时,需要传入一个gredis.Redis对象,表示要连接的Redis服务器。之后将适配器对象传给cache配置项即可。

自定义适配器

如果框架提供的适配器不能满足项目需求,gcache还支持自定义适配器。我们可以实现Adapter接口,定制数据的读写存储逻辑。

type Adapter interface {
    // Set sets cache with <key>-<value> pair, which is expired after <duration>.
    Set(ctx context.Context, key interface{}, value interface{}, duration time.Duration) error

    // Get retrieves and returns the associated value of given <key>.
    // It returns nil if it does not exist or its value is nil.
    Get(ctx context.Context, key interface{}) (interface{}, error)

    // GetExpire retrieves and returns the expiration of given <key>.
    // It returns 0 if the <key> does not expire.
    GetExpire(ctx context.Context, key interface{}) (time.Duration, error) 

    // Remove deletes one or more keys from cache, and returns its value.
    // If multiple keys are given, it returns the value of the last deleted item.
    Remove(ctx context.Context, keys ...interface{}) (value interface{}, err error)

    // Clear deletes all keys of the cache.
    Clear(ctx context.Context) error 

    // Close closes the cache if necessary.
    Close(ctx context.Context) error
}

Adapter接口定义了缓存数据的基本操作,包括SetGetRemoveClear等方法,我们需要在自定义适配器中实现这些方法。

最佳实践

为了在项目中更好地使用gcache,这里有一些建议:

  1. 统一缓存Key的定义规范,避免不同地方产生Key冲突。比如按业务领域加前缀,如user:idorder:no
  2. 缓存容量要设置得当,过大会浪费内存,太小又达不到缓存的效果,可以根据业务的需求、系统负载压力合理评估。
  3. 要缓存读多写少、制度变化频率低的数据,不要试图缓存所有数据。
  4. 对于数据一致性要求高的场景,谨慎使用缓存,防止缓存和DB数据不一致。

总结

本文介绍了GoFrame中的gcache通用缓存模块的特性和基本用法,并给出了一些使用的建议和最佳实践。gcache提供了非常实用和易用的缓存功能,可以显著提升系统性能。在实际项目中,灵活地使用好gcache,可以事半功倍。