The caching component by default provides a high-speed in-memory cache, with operation efficiency at the ns
nanosecond level of CPU performance loss.
Usage Example
Basic Usage
package main
import (
"fmt"
"github.com/gogf/gf/v2/os/gcache"
"github.com/gogf/gf/v2/os/gctx"
)
func main() {
// Create a cache object,
// but you can also conveniently use the gcache package methods directly
var (
ctx = gctx.New()
cache = gcache.New()
)
// Set cache, no expiration
err := cache.Set(ctx, "k1", "v1", 0)
if err != nil {
panic(err)
}
// Get cache value
value, err := cache.Get(ctx, "k1")
if err != nil {
panic(err)
}
fmt.Println(value)
// Get cache size
size, err := cache.Size(ctx)
if err != nil {
panic(err)
}
fmt.Println(size)
// Check if a specific key name exists in the cache
b, err := cache.Contains(ctx, "k1")
if err != nil {
panic(err)
}
fmt.Println(b)
// Remove and return the deleted key-value
removedValue, err := cache.Remove(ctx, "k1")
if err != nil {
panic(err)
}
fmt.Println(removedValue)
// Close the cache object to allow GC to reclaim resources
if err = cache.Close(ctx); err != nil {
panic(err)
}
}
After execution, the output is:
v1
1
true
v1
Expiration Control
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()
)
// Write when the key name does not exist, set expiration time to 1000 milliseconds
_, err := gcache.SetIfNotExist(ctx, "k1", "v1", time.Second)
if err != nil {
panic(err)
}
// Print current list of key names
keys, err := gcache.Keys(ctx)
if err != nil {
panic(err)
}
fmt.Println(keys)
// Print current list of key-values
values, err := gcache.Values(ctx)
if err != nil {
panic(err)
}
fmt.Println(values)
// Get a specific key value, write if it does not exist, and return the key value
value, err := gcache.GetOrSet(ctx, "k2", "v2", 0)
if err != nil {
panic(err)
}
fmt.Println(value)
// Print current key-value pairs
data1, err := gcache.Data(ctx)
if err != nil {
panic(err)
}
fmt.Println(data1)
// Wait for 1 second, so k1:v1 expires automatically
time.Sleep(time.Second)
// Print current key-value pairs again, and find that k1:v1 has expired, leaving only k2:v2
data2, err := gcache.Data(ctx)
if err != nil {
panic(err)
}
fmt.Println(data2)
}
After execution, the output is:
[k1]
[v1]
v2
map[k1:v1 k2:v2]
map[k2:v2]
GetOrSetFunc*
GetOrSetFunc
retrieves a cache value, and if the cache does not exist, executes the specified f func(context.Context) (interface{}, error)
, caches the result of the f
method, and returns that result.
It is important to note that GetOrSetFunc
‘s cache method parameter f
is executed outside of the cache’s lock mechanism, so GetOrSetFunc
can be nested within f
. However, if f
is computationally intensive, it may be executed multiple times under high concurrency (only the result from the first executed f
can be successfully cached, the rest will be discarded). On the other hand, GetOrSetFuncLock
‘s cache method f
is executed within the cache’s lock mechanism, ensuring that f
is executed only once when the cache item does not exist, but the cache write lock duration depends on the execution time of the f
method.
Here’s an example:
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(ctx context.Context) (interface{}, error) {
fmt.Println(index, "entered")
return value, nil
}, 0)
if err != nil {
panic(err)
}
}(i)
}
close(ch)
time.Sleep(time.Second)
}
After execution, the terminal outputs (random, but only one message is output):
9 entered
You can see that when multiple goroutine
s concurrently call the GetOrSetFuncLock
method, due to its concurrent safety control, only one goroutine
‘s value-generation function executes successfully, and once successful, other goroutine
s immediately return with the data without executing their corresponding value-generation functions.
LRU
Cache Eviction Control
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) // Set LRU eviction count
)
// Add 10 elements, no expiration
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)
// Read key name 1 to ensure that this key name is preferentially retained
value, err := cache.Get(ctx, 1)
if err != nil {
panic(err)
}
fmt.Println(value)
// After waiting for some time (checked once per second by default),
// elements will be evicted in order from oldest to newest
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)
}
After execution, the output is:
[2 3 5 6 7 0 1 4 8 9]
1
2
[1 9]
Performance Testing
Test Environment
CPU: Intel(R) Core(TM) i5-4460 CPU @ 3.20GHz
MEM: 8GB
SYS: Ubuntu 16.04 amd64
Test Results
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