GoFrame 緩存管理-內(nèi)存緩存

2022-04-01 10:21 更新

緩存組件默認(rèn)提供了一個(gè)高速的內(nèi)存緩存,操作效率非常高效,?CPU?性能損耗在?ns?納秒級(jí)別。

使用示例

基本使用

package main

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

func main() {
	// 創(chuàng)建一個(gè)緩存對(duì)象,
	// 當(dāng)然也可以便捷地直接使用gcache包方法
	var (
		ctx   = gctx.New()
		cache = gcache.New()
	)

	// 設(shè)置緩存,不過(guò)期
	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)

	// 關(guān)閉緩存對(duì)象,讓GC回收資源
	if err = cache.Close(ctx); err != nil {
		panic(err)
	}
}

執(zhí)行后,輸出結(jié)果為:

v1
1
true
v1

過(guò)期控制

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()
	)
	// 當(dāng)鍵名不存在時(shí)寫入,設(shè)置過(guò)期時(shí)間1000毫秒
	_, err := gcache.SetIfNotExist(ctx, "k1", "v1", time.Second)
	if err != nil {
		panic(err)
	}

	// 打印當(dāng)前的鍵名列表
	keys, err := gcache.Keys(ctx)
	if err != nil {
		panic(err)
	}
	fmt.Println(keys)

	// 打印當(dāng)前的鍵值列表
	values, err := gcache.Values(ctx)
	if err != nil {
		panic(err)
	}
	fmt.Println(values)

	// 獲取指定鍵值,如果不存在時(shí)寫入,并返回鍵值
	value, err := gcache.GetOrSet(ctx, "k2", "v2", 0)
	if err != nil {
		panic(err)
	}
	fmt.Println(value)

	// 打印當(dāng)前的鍵值對(duì)
	data1, err := gcache.Data(ctx)
	if err != nil {
		panic(err)
	}
	fmt.Println(data1)

	// 等待1秒,以便k1:v1自動(dòng)過(guò)期
	time.Sleep(time.Second)

	// 再次打印當(dāng)前的鍵值對(duì),發(fā)現(xiàn)k1:v1已經(jīng)過(guò)期,只剩下k2:v2
	data2, err := gcache.Data(ctx)
	if err != nil {
		panic(err)
	}
	fmt.Println(data2)
}

執(zhí)行后,輸出結(jié)果為:

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

GetOrSetFunc*

?GetOrSetFunc?獲取一個(gè)緩存值,當(dāng)緩存不存在時(shí)執(zhí)行指定的?f func() (interface{}, error)?,緩存該?f?方法的結(jié)果值,并返回該結(jié)果。

需要注意的是,?GetOrSetFunc?的緩存方法參數(shù)?f?是在緩存的鎖機(jī)制外執(zhí)行,因此在?f?內(nèi)部也可以嵌套調(diào)用?GetOrSetFunc?。但如果?f?的執(zhí)行比較耗時(shí),高并發(fā)的時(shí)候容易出現(xiàn)?f?被多次執(zhí)行的情況(緩存設(shè)置只有第一個(gè)執(zhí)行的?f?返回結(jié)果能夠設(shè)置成功,其余的被拋棄掉)。而?GetOrSetFuncLock?的緩存方法?f?是在緩存的鎖機(jī)制內(nèi)執(zhí)行,因此可以保證當(dāng)緩存項(xiàng)不存在時(shí)只會(huì)執(zhí)行一次?f?,但是緩存寫鎖的時(shí)間隨著?f?方法的執(zhí)行時(shí)間而定。

我們來(lái)看一個(gè)示例:

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)
}

執(zhí)行后,終端輸出(帶有隨機(jī)性,但是只會(huì)輸出一條信息):

9 entered

可以看到,多個(gè)?goroutine?同時(shí)調(diào)用?GetOrSetFuncLock?方法時(shí),由于該方法有并發(fā)安全控制,因此最終只有一個(gè)?goroutine?的數(shù)值生成函數(shù)執(zhí)行成功,成功之后其他?goroutine?拿到數(shù)據(jù)后則立即返回不再執(zhí)行對(duì)應(yīng)的數(shù)值生成函數(shù)。

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) // 設(shè)置LRU淘汰數(shù)量
	)

	// 添加10個(gè)元素,不過(guò)期
	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,保證該鍵名是優(yōu)先保留
	value, err := cache.Get(ctx, 1)
	if err != nil {
		panic(err)
	}
	fmt.Println(value)

	// 等待一定時(shí)間后(默認(rèn)1秒檢查一次),
	// 元素會(huì)被按照從舊到新的順序進(jìn)行淘汰
	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)
}

執(zhí)行后,輸出結(jié)果為:

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

性能測(cè)試

測(cè)試環(huán)境

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

測(cè)試結(jié)果

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


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)