golang高性能内存缓存支持过期与分片插件库imcache的使用

Golang高性能内存缓存支持过期与分片插件库imcache的使用

imcache是一个零依赖的通用内存缓存Go库,支持绝对过期、滑动过期、最大条目限制、驱逐回调和分片功能。它是并发安全的,可以被多个goroutine同时使用。

基本用法

package main

import (
	"fmt"

	"github.com/erni27/imcache"
)

func main() {
	// 零值Cache是一个有效的非分片缓存
	// 没有过期时间,没有滑动过期,
	// 没有条目限制和没有驱逐回调
	var c imcache.Cache[uint32, string]
	c.Set(1, "one", imcache.WithNoExpiration())
	value, ok := c.Get(1)
	if !ok {
		panic("value for the key '1' not found")
	}
	fmt.Println(value)
}

过期策略

imcache支持以下过期选项:

  • WithNoExpiration - 条目永不过期
  • WithExpiration - 条目在一定时间后过期
  • WithExpirationDate - 条目在特定日期过期
  • WithSlidingExpiration - 如果条目未被访问,则在一定时间后过期。每次访问条目时,过期时间会重置为当前时间加上滑动过期时间
// 条目永不过期
c.Set(1, "one", imcache.WithNoExpiration())
// 条目在1秒后过期
c.Set(2, "two", imcache.WithExpiration(time.Second))
// 条目在指定日期过期
c.Set(3, "three", imcache.WithExpirationDate(time.Now().Add(time.Second)))
// 如果条目未被访问,则在1秒后过期
// 否则,过期时间将滑动到访问时间+1秒
c.Set(4, "four", imcache.WithSlidingExpiration(time.Second))

键驱逐

imcache会主动驱逐过期条目。当通过大多数Cache方法(读写操作)访问条目时,会移除过期条目。PeekPeekMultiplePeekAll方法是例外,它们不会移除过期条目,也不会滑动过期时间(如果设置了滑动过期)。

可以使用Cleaner定期从缓存中移除过期条目。Cleaner是一个后台goroutine,定期从缓存中移除过期条目。默认情况下Cleaner是禁用的。可以使用WithCleanerOption选项启用Cleaner并设置清理间隔。

// 创建一个新的Cache,带有每5分钟清理一次过期条目的Cleaner
c := imcache.New[string, string](imcache.WithCleanerOption[string, string](5 * time.Minute))
// 关闭Cache。如果Cleaner正在运行,这将停止它
defer c.Close()

最大条目限制

imcache支持设置最大条目限制。当达到最大条目限制时,根据选择的驱逐策略驱逐条目。imcache支持以下驱逐策略:

  • EvictionPolicyLRU - 驱逐最近最少使用的条目
  • EvictionPolicyLFU - 驱逐最不经常使用的条目
  • EvictionPolicyRandom - 随机驱逐一个条目

可以使用WithMaxEntriesLimitOption选项为给定的缓存实例设置最大条目限制和驱逐策略。

c := imcache.New[uint32, string](imcache.WithMaxEntriesLimitOption[uint32, string](1000, imcache.EvictionPolicyLRU))

分片

imcache支持分片。每个分片是一个单独的Cache实例。通过计算键的哈希值并对分片数取模来选择给定键的分片。imcache公开了Hasher64接口,可用于实现自定义分片算法。

可以通过调用NewSharded方法创建一个Sharded实例。

c := imcache.NewSharded[string, string](4, imcache.DefaultStringHasher64{})

完整示例

package main

import (
	"log"
	"time"

	"github.com/erni27/imcache"
)

func LogEvictedEntry(key string, value interface{}, reason imcache.EvictionReason) {
	log.Printf("Evicted entry: %s=%v (%s)", key, value, reason)
}

func main() {
	// 创建一个带有1秒默认过期时间和驱逐回调的缓存
	c := imcache.New[string, interface{}](
		imcache.WithDefaultExpirationOption[string, interface{}](time.Second),
		imcache.WithEvictionCallbackOption[string, interface{}](LogEvictedEntry),
	)
	c.Set("foo", "bar", imcache.WithDefaultExpiration())

	time.Sleep(time.Second)

	_, ok := c.Get("foo")
	if ok {
		panic("expected entry to be expired")
	}
}

性能

imcache与具有简单锁定机制的普通Go map进行了比较。基准测试在Apple M1 Pro 8核CPU、32GB内存、macOS Ventura 13.4.1上运行,使用Go 1.21.6。

读取性能

BenchmarkCache_Get-8                                                                 2655246	       428.5 ns/op
BenchmarkSharded_Get/2_Shards-8                                                      2810713	       436.8 ns/op
BenchmarkSharded_Get/4_Shards-8                                                      2732820	       444.9 ns/op
BenchmarkSharded_Get/8_Shards-8                                                      2957444	       445.7 ns/op
BenchmarkSharded_Get/16_Shards-8                                                     2773999	       447.0 ns/op
BenchmarkSharded_Get/32_Shards-8                                                     2752075	       443.4 ns/op
BenchmarkSharded_Get/64_Shards-8                                                     2752899	       439.7 ns/op
BenchmarkSharded_Get/128_Shards-8                                                    2771691	       456.3 ns/op

写入性能

BenchmarkCache_Set-8                                                                 2942062	       461.7 ns/op
BenchmarkSharded_Set/2_Shards-8                                                      2939275	       487.5 ns/op
BenchmarkSharded_Set/4_Shards-8                                                      2827146	       497.1 ns/op
BenchmarkSharded_Set/8_Shards-8                                                      2837316	       509.1 ns/op
BenchmarkSharded_Set/16_Shards-8                                                     2818513	       495.7 ns/op
BenchmarkSharded_Set/32_Shards-8                                                     2793490	       506.3 ns/op
BenchmarkSharded_Set/64_Shards-8                                                     2815544	       499.7 ns/op
BenchmarkSharded_Set/128_Shards-8                                                    2738779	       511.2 ns/op

imcache提供了高性能的内存缓存解决方案,支持多种过期策略和分片功能,非常适合需要高性能缓存的Go应用程序。


更多关于golang高性能内存缓存支持过期与分片插件库imcache的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang高性能内存缓存支持过期与分片插件库imcache的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang高性能内存缓存库imcache使用指南

imcache是一个高性能的Go内存缓存库,支持过期时间和分片功能。下面我将详细介绍如何使用imcache。

基本特性

  • 支持键值过期(TTL)
  • 自动清理过期项目
  • 分片支持提高并发性能
  • 简单易用的API

安装

go get github.com/erni27/imcache

基本使用

1. 创建缓存

package main

import (
	"fmt"
	"time"
	
	"github.com/erni27/imcache"
)

func main() {
	// 创建一个简单的缓存
	cache := imcache.New[string, string]()
	
	// 设置带过期时间的值
	cache.Set("key1", "value1", imcache.WithDefaultExpiration())
	
	// 设置自定义过期时间
	cache.Set("key2", "value2", imcache.WithExpiration(time.Hour))
	
	// 获取值
	if val, ok := cache.Get("key1"); ok {
		fmt.Println("Got value:", val)
	}
	
	// 删除键
	cache.Delete("key1")
}

2. 带分片的缓存

func main() {
	// 创建带分片的缓存(16个分片)
	shardedCache := imcache.NewSharded[string, int](16)
	
	// 并发设置值
	for i := 0; i < 100; i++ {
		go func(i int) {
			key := fmt.Sprintf("key%d", i)
			shardedCache.Set(key, i, imcache.WithExpiration(time.Minute))
		}(i)
	}
	
	time.Sleep(time.Second) // 等待goroutine完成
	
	// 获取所有值
	for i := 0; i < 100; i++ {
		key := fmt.Sprintf("key%d", i)
		if val, ok := shardedCache.Get(key); ok {
			fmt.Printf("%s: %d\n", key, val)
		}
	}
}

3. 高级配置

func main() {
	// 自定义配置
	cache := imcache.New[string, interface{}](
		imcache.WithDefaultExpirationOption(time.Minute*5), // 默认过期时间
		imcache.WithCleanupIntervalOption(time.Minute),      // 清理间隔
		imcache.WithOnEvictedFunc(func(key string, value interface{}) {
			fmt.Printf("Key %s evicted\n", key)
		}),
	)
	
	cache.Set("user:1001", map[string]interface{}{
		"name":  "Alice",
		"email": "alice@example.com",
	}, imcache.WithExpiration(time.Hour))
	
	if user, ok := cache.Get("user:1001"); ok {
		fmt.Println("User:", user)
	}
}

4. 性能优化技巧

  1. 合理设置分片数量:根据CPU核心数和并发量设置

    // 通常设置为CPU核心数的2-4倍
    shardedCache := imcache.NewSharded[string, int](runtime.NumCPU() * 2)
    
  2. 批量操作

    // 批量设置
    items := map[string]string{
        "key1": "value1",
        "key2": "value2",
    }
    for k, v := range items {
        cache.Set(k, v, imcache.WithDefaultExpiration())
    }
    
  3. 使用指针类型存储大对象减少拷贝:

    type BigStruct struct {
        // 大字段
    }
    
    cache := imcache.New[string, *BigStruct]()
    bigObj := &BigStruct{...}
    cache.Set("big", bigObj, imcache.WithDefaultExpiration())
    

注意事项

  1. imcache是内存缓存,重启后数据会丢失
  2. 大量数据可能导致内存压力,需监控内存使用
  3. 对于复杂查询需求,可能需要结合其他数据结构

imcache适合作为应用层的快速缓存解决方案,特别是需要高并发访问的场景。对于分布式环境,可以考虑结合Redis等分布式缓存使用。

回到顶部