go ttlmap实现示例
在某些情况下,您的应用程序并不需要 Redis,内部带有锁和过期机制的内存映射就足以满足需求。
例如,当您已知映射的大小且不需要存储大量数据时。适用场景包括 IP 速率限制或其他短期数据存储。
以下是如何在 Go 中实现这种数据结构,我们称之为 TTLMap:
package ttlmap
import (
"sync"
"time"
)
// item 结构体用于保存值和最后访问时间
type item struct {
value interface{}
lastAccess int64
}
// 可以针对整个应用程序使用单个映射,或针对不同用途创建多个映射
type TTLMap struct {
m map[string]*item // 存储实际数据的映射
mu sync.Mutex // 确保线程安全的互斥锁
}
// New 创建指定大小和最大 TTL 的映射
func New(size int, maxTTL int) (m *TTLMap) {
m = &TTLMap{m: make(map[string]*item, size)}
// 这个 goroutine 会定期清理映射中的旧条目
go func() {
for now := range time.Tick(time.Second) { // 可调整定时器频率
m.mu.Lock()
for k, v := range m.m {
if now.Unix()-v.lastAccess > int64(maxTTL) {
delete(m.m, k) // 删除过期条目
}
}
m.mu.Unlock()
}()
}
return
}
// Put 添加新条目或更新现有条目
func (m *TTLMap) Put(k string, v interface{}) {
m.mu.Lock()
defer m.mu.Unlock()
it, ok := m.m[k]
if !ok {
it = &item{
value: v,
}
}
it.value = v
it.lastAccess = time.Now().Unix() // 更新访问时间
m.m[k] = it
}
// Get 返回指定键的值(如果存在)
func (m *TTLMap) Get(k string) (interface{}, bool) {
m.mu.Lock()
defer m.mu.Unlock()
if it, ok := m.m[k]; ok {
it.lastAccess = time.Now().Unix() // 更新访问时间
return it.value, true
}
return nil, false
}
// Delete 从映射中移除条目
func (m *TTLMap) Delete(k string) {
m.mu.Lock()
defer m.mu.Unlock()
if _, ok := m.m[k]; ok {
delete(m.m, k) // 直接删除条目
}
}
该映射支持并发安全访问,并且每秒会自动清理过期条目(可通过调整 time.Tick 频率改变清理频率)。虽然它明显缺乏 Redis 的诸多特性,但对于简单场景(如 IP 限流)来说已经足够。目前仅提供 Put、Get、Delete 三个基础方法,没有通配符等高级功能。如需更复杂功能,建议使用 Redis 或其他键值存储系统。
使用示例:
// 创建容量为100条目、最大TTL为10秒的映射
m := ttlmap.New(100, 10)
m.Put("key1", "字符串值")
v, ok := m.Get("key1") // v == "字符串值", ok == true
m.Put("key2", 42)
v, ok = m.Get("key3") // v == nil, ok == false

浙公网安备 33010602011771号