前言
在独立开发项目时,我遇到了一个需求:维护一个集合,其中的元素在一段时间内会过期,同时支持用户自定义对集合的增删改查操作。通常,这类需求会使用Redis自带的expire功能来实现。然而,使用Redis会引入额外的运维成本,同时增加接口响应时间,尤其是对于不需要Redis这种额外依赖的场景。
目前市场上许多开源的TTL Map实现都较为复杂,同时缺乏对TTL生命周期的有效管理。经过综合考虑,决定自己实现一个简单且易用的基于TTL的Map,其用法与Golang原生Map类似,以减少开发和维护成本。
TTL Map
此库具有以下显著特点:
- 在某些场景下,性能几乎与Golang的Map无异。
- 提供精确的过期时间控制。
- 支持更符合业务需求的
tryDelete操作。 - 强大的Drain策略,有效清理过期元素。
- 支持泛型,可以灵活使用不同类型的键值对。
性能测试报告
在性能测试中,ttlmap展示了出色的表现,具体结果如下图所示:
TTL Map 设计理念与细节
-
底层交由Map处理
与许多复杂的TTL Map实现不同,本库直接基于Golang原生Map进行操作。许多TTL Map会通过分块等机制优化性能,但经过对比后发现,这样的做法在许多场景下并未带来显著提升,反而会增加实现的复杂性。在不涉及极高并发的场景下,直接使用Golang的Map处理更加高效,且简化了设计。 -
更简洁的生命周期管理
本库内置了协程管理,用户无需显式管理协程的创建与销毁。使用Drain方法来释放所有相关资源,确保TTL Map可以被垃圾回收。此设计简化了内存管理,同时减少了用户出错的可能性。整个设计中,我们采用了nocopy和cant equal策略,确保了稳定性和性能。 -
简单易用的接口设计
我们的目标是让用户的使用体验与Golang原生Map尽可能一致。因此,ttlmap的接口设计简洁明了,使用非常直观。以下是一个简单示例:
package main
import (
"fmt"
"github.com/0xdoomxy/ttlmap"
"time"
)
func main() {
// 设置全局过期时间
var globalTTL = 3 * time.Second
// 创建一个TTL Map实例,设置全局过期时间和刷新时间
tm := ttlmap.NewTTLMap[string, string](ttlmap.WithTTL[string, string](globalTTL), ttlmap.WithFlushInterval[string, string](1*time.Second))
// 设置带有自定义过期时间的键值对
tm.SetWithExpire("1", "2", time.Minute)
// 设置键值对
tm.Set("1", "2")
// 删除键值对
tm.Delete("1")
// 尝试删除键并返回值
val, ok := tm.TryDelete("1")
if ok {
fmt.Println(val)
}
// 获取键值
val = tm.Get("1")
fmt.Println(val)
// 尝试获取键值
val, ok = tm.TryGet("1")
if ok {
fmt.Println(val)
}
// 释放资源,确保Map可以被GC回收
tm.Drain()
}
总结
TTL Map是一个轻量级、高效、易用的解决方案,适用于需要键值对过期管理的应用场景。其简洁的API和高性能表现,使得它成为一个理想的替代方案,特别适用于无需引入额外数据库的轻量级缓存需求。
欢迎大家前往GitHub项目页面获取更多详情和代码示例,欢迎各位大佬提出建议!