golang实现带过期时间的键值对存储插件库timedmap的使用

Golang实现带过期时间的键值对存储插件库timedmap的使用

介绍

timedmap是一个带有过期键值对的map结构,它允许设置在一定时间后自动过期的键值对。当键值对过期时,可以执行回调函数。

安装

go get -u github.com/zekroTJA/timedmap/v2

重要说明

v2版本引入了以下重大变更:

  • 需要Go 1.19或更高版本
  • TimedMap和构造函数现在使用类型参数以提高类型安全性
  • 移除了Sections以提高性能和简化包
  • 移除了之前已弃用的函数

使用示例

下面是一个完整的示例代码,展示了如何使用timedmap:

package main

import (
	"log"
	"time"

	"github.com/zekroTJA/timedmap/v2"
)

func main() {

	// 创建一个新的timedmap,每隔1秒扫描一次过期的键
	tm := timedmap.New[string, int](1 * time.Second)

	// 添加一个键"hey",值为213,3秒后过期
	// 过期时会执行回调函数,打印过期信息
	tm.Set("hey", 213, 3*time.Second, func(v int) {
		log.Println("key-value pair of 'hey' has expired")
	})

	// 打印键"hey"的值
	printKeyVal(tm, "hey")

	// 等待5秒
	// 在此期间主线程被阻塞,"hey"键值对将会过期
	time.Sleep(5 * time.Second)

	// 再次尝试打印键"hey"的值,此时会失败因为键值对已经过期
	printKeyVal(tm, "hey")
}

// 打印指定键的值
func printKeyVal(tm *timedmap.TimedMap[string, int], key string) {
	d, ok := tm.GetValue(key)
	if !ok {
		log.Println("data expired")
		return
	}

	log.Printf("%v = %d\n", key, d)
}

代码说明

  1. 首先导入必要的包,包括timedmap/v2
  2. 使用timedmap.New创建一个新的TimedMap实例,指定键和值的类型以及扫描间隔
  3. 使用Set方法添加键值对,指定过期时间和过期回调函数
  4. 使用GetValue方法获取键对应的值,返回值和是否存在标志
  5. 当键值对过期时,回调函数会自动执行

实际应用

timedmap可以用于多种场景,如:

  • 缓存系统
  • 限流器
  • 临时数据存储

这个包已经被用于myrunes.com的REST API限流实现中。


更多关于golang实现带过期时间的键值对存储插件库timedmap的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现带过期时间的键值对存储插件库timedmap的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 实现带过期时间的键值对存储 - timedmap 使用指南

在 Go 中实现带过期时间的键值对存储是一个常见需求,下面我将介绍如何使用 timedmap 库来实现这一功能。

timedmap 简介

timedmap 是一个 Go 语言实现的键值存储库,主要特点是:

  • 自动处理键值对的过期
  • 线程安全
  • 提供回调函数支持
  • 简单易用的 API

安装

go get github.com/zekroTJA/timedmap

基本使用示例

package main

import (
	"fmt"
	"time"
	
	"github.com/zekroTJA/timedmap"
)

func main() {
	// 创建一个新的 timedmap 实例,设置默认清理间隔为1秒
	tm := timedmap.New(1 * time.Second)
	
	// 设置键值对,5秒后过期
	tm.Set("key1", "value1", 5*time.Second, nil)
	
	// 获取值
	if val, ok := tm.GetValue("key1").(string); ok {
		fmt.Println("Got value:", val) // 输出: Got value: value1
	}
	
	// 等待6秒后键将自动过期
	time.Sleep(6 * time.Second)
	
	if val := tm.GetValue("key1"); val == nil {
		fmt.Println("Key expired") // 输出: Key expired
	}
}

高级功能

1. 设置过期回调函数

tm.Set("key2", "value2", 3*time.Second, func(value interface{}) {
    fmt.Printf("Key 'key2' expired with value: %v\n", value)
})

2. 手动删除键

tm.Remove("key1")

3. 刷新键的过期时间

// 将key1的过期时间重置为10秒后
tm.Refresh("key1", 10*time.Second)

4. 获取剩余生存时间

if ttl := tm.GetExpires("key1"); ttl > 0 {
    fmt.Printf("Key will expire in %v\n", ttl)
}

5. 批量操作

// 批量设置
tm.SetMultiple(map[string]interface{}{
    "key3": "value3",
    "key4": "value4",
}, 5*time.Second)

// 获取所有键
keys := tm.Keys()
fmt.Println("All keys:", keys)

实际应用示例

package main

import (
	"fmt"
	"time"
	
	"github.com/zekroTJA/timedmap"
)

func main() {
	// 创建缓存实例,设置清理间隔为1秒
	cache := timedmap.New(1 * time.Second)
	
	// 模拟用户会话存储
	cache.Set("user:123", map[string]interface{}{
		"name":  "Alice",
		"email": "alice@example.com",
		"token": "abc123xyz",
	}, 30*time.Minute, func(value interface{}) {
		user := value.(map[string]interface{})
		fmt.Printf("User session expired: %s (%s)\n", user["name"], user["email"])
	})
	
	// 模拟API请求
	go func() {
		time.Sleep(5 * time.Second)
		if user, ok := cache.GetValue("user:123").(map[string]interface{}); ok {
			fmt.Printf("API request from %s\n", user["name"])
			// 每次访问后刷新会话
			cache.Refresh("user:123", 30*time.Minute)
		}
	}()
	
	// 保持程序运行
	time.Sleep(1 * time.Hour)
}

性能考虑

  1. 清理间隔 (New() 函数的参数) 会影响性能:

    • 间隔越短,过期检测越及时,但CPU使用率越高
    • 间隔越长,内存可能短暂保留已过期的键,但CPU使用率低
  2. 对于高并发场景,timedmap 内部使用读写锁保证线程安全

替代方案

如果需要更高级的功能,也可以考虑:

  • github.com/patrickmn/go-cache - 功能更丰富的内存缓存
  • Redis - 如果需要在多实例间共享缓存

timedmap 是一个轻量级解决方案,适合简单的内存缓存需求,特别是需要精确控制键过期时间的场景。

回到顶部