gcache模块默认提供的是一个高速的内存缓存,操作效率非常高效,CPU性能损耗在ns纳秒级别。

示例1,基本使用

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/os/gcache"
  5. )
  6. func main() {
  7. // 创建一个缓存对象,
  8. // 当然也可以便捷地直接使用gcache包方法
  9. c := gcache.New()
  10. // 设置缓存,不过期
  11. c.Set("k1", "v1", 0)
  12. // 获取缓存
  13. v, _ := c.Get("k1")
  14. fmt.Println(v)
  15. // 获取缓存大小
  16. n, _ := c.Size()
  17. fmt.Println(n)
  18. // 缓存中是否存在指定键名
  19. b, _ := c.Contains("k1")
  20. fmt.Println(b)
  21. // 删除并返回被删除的键值
  22. fmt.Println(c.Remove("k1"))
  23. // 关闭缓存对象,让GC回收资源
  24. c.Close()
  25. }

执行后,输出结果为:

  1. v1
  2. 1
  3. true
  4. v1

示例2,缓存控制

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/os/gcache"
  5. "time"
  6. )
  7. func main() {
  8. // 当键名不存在时写入,设置过期时间1000毫秒
  9. gcache.SetIfNotExist("k1", "v1", 1000*time.Millisecond)
  10. // 打印当前的键名列表
  11. keys, _ := gcache.Keys()
  12. fmt.Println(keys)
  13. // 打印当前的键值列表
  14. values, _ := gcache.Values()
  15. fmt.Println(values)
  16. // 获取指定键值,如果不存在时写入,并返回键值
  17. v, _ := gcache.GetOrSet("k2", "v2", 0)
  18. fmt.Println(v)
  19. // 打印当前的键值对
  20. data1, _ := gcache.Data()
  21. fmt.Println(data1)
  22. // 等待1秒,以便k1:v1自动过期
  23. time.Sleep(time.Second)
  24. // 再次打印当前的键值对,发现k1:v1已经过期,只剩下k2:v2
  25. data2, _ := gcache.Data()
  26. fmt.Println(data2)
  27. }

执行后,输出结果为:

  1. [k1]
  2. [v1]
  3. v2
  4. map[k1:v1 k2:v2]
  5. map[k2:v2]

示例3,GetOrSetFunc*

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

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

GetOrSetFuncLock的缓存方法f是在缓存的锁机制内执行,因此可以保证当缓存项不存在时只会执行一次f,但是缓存写锁的时间随着f方法的执行时间而定。

我们来看一个在gf-home项目中使用GetOrSetFunc的示例,该示例遍历检索markdown文件进行字符串检索,并根据指定的搜索key缓存该结果值,因此多次搜索该key时,第一次会执行目录遍历搜索,后续将直接使用缓存结果。

  1. // 根据关键字进行markdown文档搜索,返回文档path列表
  2. func SearchMdByKey(key string) ([]string, error) {
  3. v, err := gcache.GetOrSetFunc("doc_search_result_"+key, func() (interface{}, error) {
  4. // 当该key的检索缓存不存在时,执行检索
  5. array := garray.NewStrArray()
  6. docPath := g.Cfg().GetString("doc.path")
  7. paths, err := gcache.GetOrSetFunc("doc_files_recursive", func() (interface{}, error) {
  8. // 当目录列表不存在时,执行检索
  9. return gfile.ScanDir(docPath, "*.md", true)
  10. }, 0)
  11. if err != nil {
  12. return nil, err
  13. }
  14. // 遍历markdown文件列表,执行字符串搜索
  15. for _, path := range gconv.Strings(paths) {
  16. content := gfile.GetContents(path)
  17. if len(content) > 0 {
  18. if strings.Index(content, key) != -1 {
  19. index := gstr.Replace(path, ".md", "")
  20. index = gstr.Replace(index, docPath, "")
  21. array.Append(index)
  22. }
  23. }
  24. }
  25. return array.Slice(), nil
  26. }, 0)
  27. if err != nil {
  28. return nil, err
  29. }
  30. return gconv.Strings(v), nil
  31. }

示例4,LRU缓存淘汰控制

  1. package main
  2. import (
  3. "github.com/gogf/gf/os/gcache"
  4. "time"
  5. "fmt"
  6. )
  7. func main() {
  8. // 设置LRU淘汰数量
  9. c := gcache.New(2)
  10. // 添加10个元素,不过期
  11. for i := 0; i < 10; i++ {
  12. c.Set(i, i, 0)
  13. }
  14. n, _ := c.Size()
  15. fmt.Println(n)
  16. keys, _ := c.Keys()
  17. fmt.Println(keys)
  18. // 读取键名1,保证该键名是优先保留
  19. v, _ := c.Get(1)
  20. fmt.Println(v)
  21. // 等待一定时间后(默认1秒检查一次),
  22. // 元素会被按照从旧到新的顺序进行淘汰
  23. time.Sleep(2*time.Second)
  24. n, _ = c.Size()
  25. fmt.Println(n)
  26. keys, _ = c.Keys()
  27. fmt.Println(keys)
  28. }

执行后,输出结果为:

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

性能测试

测试环境

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

测试结果

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