Categories: GoFrame 教程

GoFrame 缓存管理-内存缓存

缓存组件默认提供了一个高速的内存缓存,操作效率非常高效,​CPU​性能损耗在​ns​纳秒级别。

使用示例

基本使用

package main

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

func main() {
 // 创建一个缓存对象,
 // 当然也可以便捷地直接使用gcache包方法
 var (
  ctx   = gctx.New()
  cache = gcache.New()
 )

 // 设置缓存,不过期
 err := cache.Set(ctx, "k1", "v1", 0)
 if err != nil {
  panic(err)
 }

 // 获取缓存值
 value, err := cache.Get(ctx, "k1")
 if err != nil {
  panic(err)
 }
 fmt.Println(value)

 // 获取缓存大小
 size, err := cache.Size(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(size)

 // 缓存中是否存在指定键名
 b, err := cache.Contains(ctx, "k1")
 if err != nil {
  panic(err)
 }
 fmt.Println(b)

 // 删除并返回被删除的键值
 removedValue, err := cache.Remove(ctx, "k1")
 if err != nil {
  panic(err)
 }
 fmt.Println(removedValue)

 // 关闭缓存对象,让GC回收资源
 if err = cache.Close(ctx); err != nil {
  panic(err)
 }
}

执行后,输出结果为:

v1
1
true
v1

过期控制

package main

import (
 "fmt"
 "github.com/gogf/gf/v2/os/gcache"
 "github.com/gogf/gf/v2/os/gctx"
 "time"
)

func main() {
 var (
  ctx = gctx.New()
 )
 // 当键名不存在时写入,设置过期时间1000毫秒
 _, err := gcache.SetIfNotExist(ctx, "k1", "v1", time.Second)
 if err != nil {
  panic(err)
 }

 // 打印当前的键名列表
 keys, err := gcache.Keys(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(keys)

 // 打印当前的键值列表
 values, err := gcache.Values(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(values)

 // 获取指定键值,如果不存在时写入,并返回键值
 value, err := gcache.GetOrSet(ctx, "k2", "v2", 0)
 if err != nil {
  panic(err)
 }
 fmt.Println(value)

 // 打印当前的键值对
 data1, err := gcache.Data(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(data1)

 // 等待1秒,以便k1:v1自动过期
 time.Sleep(time.Second)

 // 再次打印当前的键值对,发现k1:v1已经过期,只剩下k2:v2
 data2, err := gcache.Data(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(data2)
}

执行后,输出结果为:

[k1]
[v1]
v2
map[k1:v1 k2:v2]
map[k2:v2]

GetOrSetFunc*

GetOrSetFunc​获取一个缓存值,当缓存不存在时执行指定的​f func() (interface{}, error)​,缓存该​f​方法的结果值,并返回该结果。

需要注意的是,​GetOrSetFunc​的缓存方法参数​f​是在缓存的锁机制外执行,因此在​f​内部也可以嵌套调用​GetOrSetFunc​。但如果​f​的执行比较耗时,高并发的时候容易出现​f​被多次执行的情况(缓存设置只有第一个执行的​f​返回结果能够设置成功,其余的被抛弃掉)。而​GetOrSetFuncLock​的缓存方法​f​是在缓存的锁机制内执行,因此可以保证当缓存项不存在时只会执行一次​f​,但是缓存写锁的时间随着​f​方法的执行时间而定。

我们来看一个示例:

package main

import (
 "fmt"
 "github.com/gogf/gf/v2/os/gcache"
 "github.com/gogf/gf/v2/os/gctx"
 "time"
)

func main() {
 var (
  ch    = make(chan struct{}, 0)
  ctx   = gctx.New()
  key   = `key`
  value = `value`
 )
 for i := 0; i < 10; i++ {
  go func(index int) {
   <-ch
   _, err := gcache.GetOrSetFuncLock(ctx, key, func() (interface{}, error) {
    fmt.Println(index, "entered")
    return value, nil
   }, 0)
   if err != nil {
    panic(err)
   }
  }(i)
 }
 close(ch)
 time.Sleep(time.Second)
}

执行后,终端输出(带有随机性,但是只会输出一条信息):

9 entered

可以看到,多个​goroutine​同时调用​GetOrSetFuncLock​方法时,由于该方法有并发安全控制,因此最终只有一个​goroutine​的数值生成函数执行成功,成功之后其他​goroutine​拿到数据后则立即返回不再执行对应的数值生成函数。

LRU缓存淘汰控制

package main

import (
 "fmt"
 "github.com/gogf/gf/v2/os/gcache"
 "github.com/gogf/gf/v2/os/gctx"
 "time"
)

func main() {

 var (
  ctx   = gctx.New()
  cache = gcache.New(2) // 设置LRU淘汰数量
 )

 // 添加10个元素,不过期
 for i := 0; i < 10; i++ {
  if err := cache.Set(ctx, i, i, 0); err != nil {
   panic(err)
  }
 }
 size, err := cache.Size(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(size)

 keys, err := cache.Keys(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(keys)

 // 读取键名1,保证该键名是优先保留
 value, err := cache.Get(ctx, 1)
 if err != nil {
  panic(err)
 }
 fmt.Println(value)

 // 等待一定时间后(默认1秒检查一次),
 // 元素会被按照从旧到新的顺序进行淘汰
 time.Sleep(3 * time.Second)
 size, err = cache.Size(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(size)

 keys, err = cache.Keys(ctx)
 if err != nil {
  panic(err)
 }
 fmt.Println(keys)
}

执行后,输出结果为:

10
[2 3 5 6 7 0 1 4 8 9]
1
2
[1 9]

性能测试

测试环境

CPU: Intel(R) Core(TM) i5-4460  CPU @ 3.20GHz
MEM: 8GB
SYS: Ubuntu 16.04 amd64

测试结果

john@john-B85M:~/Workspace/Go/GOPATH/src/github.com/gogf/gf/v2/os/gcache$ go test *.go -bench=".*" -benchmem
goos: linux
goarch: amd64
Benchmark_CacheSet-4                       2000000        897 ns/op      249 B/op        4 allocs/op
Benchmark_CacheGet-4                       5000000        202 ns/op       49 B/op        1 allocs/op
Benchmark_CacheRemove-4                   50000000       35.7 ns/op        0 B/op        0 allocs/op
Benchmark_CacheLruSet-4                    2000000        880 ns/op      399 B/op        4 allocs/op
Benchmark_CacheLruGet-4                    3000000        212 ns/op       33 B/op        1 allocs/op
Benchmark_CacheLruRemove-4                50000000       35.9 ns/op        0 B/op        0 allocs/op
Benchmark_InterfaceMapWithLockSet-4        3000000        477 ns/op       73 B/op        2 allocs/op
Benchmark_InterfaceMapWithLockGet-4       10000000        149 ns/op        0 B/op        0 allocs/op
Benchmark_InterfaceMapWithLockRemove-4    50000000       39.8 ns/op        0 B/op        0 allocs/op
Benchmark_IntMapWithLockWithLockSet-4      5000000        304 ns/op       53 B/op        0 allocs/op
Benchmark_IntMapWithLockGet-4             20000000        164 ns/op        0 B/op        0 allocs/op
Benchmark_IntMapWithLockRemove-4          50000000       33.1 ns/op        0 B/op        0 allocs/op
PASS
ok   command-line-arguments 47.503s

冒牌SEO

前端开发者,欢迎大家一起沟通和交流。

Share
Published by
冒牌SEO

Recent Posts

聊聊vue3中的defineProps

在Vue 3中,defineP…

1 周 ago

在 Chrome 中删除、允许和管理 Cookie

您可以选择删除现有 Cooki…

2 周 ago

自定义指令:聊聊vue中的自定义指令应用法则

今天我们来聊聊vue中的自定义…

3 周 ago

聊聊Vue中@click.stop和@click.prevent

一起来学下聊聊Vue中@cli…

4 周 ago

Nginx 基本操作:启动、停止、重启命令。

我们来学习Nginx基础操作:…

1 月 ago

Vue3:手动清理keep-alive组件缓存的方法

Vue3中手动清理keep-a…

1 月 ago