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接口定义了缓存数据的基本操作,包括Set、Get、Remove、Clear等方法,我们需要在自定义适配器中实现这些方法。
最佳实践
为了在项目中更好地使用gcache,这里有一些建议:
- 统一缓存Key的定义规范,避免不同地方产生Key冲突。比如按业务领域加前缀,如
user:id、order:no。 - 缓存容量要设置得当,过大会浪费内存,太小又达不到缓存的效果,可以根据业务的需求、系统负载压力合理评估。
- 要缓存读多写少、制度变化频率低的数据,不要试图缓存所有数据。
- 对于数据一致性要求高的场景,谨慎使用缓存,防止缓存和DB数据不一致。
总结
本文介绍了GoFrame中的gcache通用缓存模块的特性和基本用法,并给出了一些使用的建议和最佳实践。gcache提供了非常实用和易用的缓存功能,可以显著提升系统性能。在实际项目中,灵活地使用好gcache,可以事半功倍。