Data structures 在Go中使用TTL选项进行映射
我需要构建如下数据结构:Data structures 在Go中使用TTL选项进行映射,data-structures,go,timeout,ttl,Data Structures,Go,Timeout,Ttl,我需要构建如下数据结构: map[string]SomeType 但它必须将值存储大约10分钟,然后从内存中清除。 第二个条件是记录数量——它一定是巨大的。此数据结构必须每秒添加至少2-5K条记录 那么,围棋中最正确的方法是什么呢 我正在尝试为每个新元素创建一个超时的goroutine。以及一个(或多个)垃圾收集器goroutine,它具有接收超时和清除元素的通道。 但我不确定这是最清晰的方式。数百万次等待超时的goroutine可以吗 谢谢。您必须创建一个结构来保存地图,并提供自定义的ge
map[string]SomeType
但它必须将值存储大约10分钟,然后从内存中清除。
第二个条件是记录数量——它一定是巨大的。此数据结构必须每秒添加至少2-5K条记录
那么,围棋中最正确的方法是什么呢
我正在尝试为每个新元素创建一个超时的goroutine。以及一个(或多个)垃圾收集器goroutine,它具有接收超时和清除元素的通道。
但我不确定这是最清晰的方式。数百万次等待超时的goroutine可以吗
谢谢。您必须创建一个结构来保存地图,并提供自定义的get/put/delete函数来访问地图 请注意,每秒2-5k的访问量实际上并没有那么多,所以您不必担心这一点 下面是一个简单的实现:
type item struct {
value string
lastAccess int64
}
type TTLMap struct {
m map[string]*item
l sync.Mutex
}
func New(ln int, maxTTL int) (m *TTLMap) {
m = &TTLMap{m: make(map[string]*item, ln)}
go func() {
for now := range time.Tick(time.Second) {
m.l.Lock()
for k, v := range m.m {
if now.Unix() - v.lastAccess > int64(maxTTL) {
delete(m.m, k)
}
}
m.l.Unlock()
}
}()
return
}
func (m *TTLMap) Len() int {
return len(m.m)
}
func (m *TTLMap) Put(k, v string) {
m.l.Lock()
it, ok := m.m[k]
if !ok {
it = &item{value: v}
m.m[k] = it
}
it.lastAccess = time.Now().Unix()
m.l.Unlock()
}
func (m *TTLMap) Get(k string) (v string) {
m.l.Lock()
if it, ok := m.m[k]; ok {
v = it.value
it.lastAccess = time.Now().Unix()
}
m.l.Unlock()
return
}
注意(2020-09-23):由于某些原因,当前版本的操场上的时间分辨率相差很远,这很好,但是要在操场上尝试,您必须将睡眠时间更改为3-5秒。看一看
tinykv
不再被维护
为了记录在案,我也遇到了同样的问题,并编写了一个在内部使用映射的包
- 它使用一堆
来超时,因此它不会覆盖整个映射time.time
- 创建实例时可以设置最大间隔。但检查超时的实际间隔可以是
time.Duration的任意值,根据最后一个超时项目,该值大于零且小于max
- 它提供
和CAS
功能Take
- 可以设置一个回调(可选),通知哪个键和值超时
- 超时可以是显式的,也可以是滑动的
sync
,它非常易于使用,并且已经可以处理并发性如果您需要这种TTL行为,您不会想要使用Map
,或者至少您需要在Map
之外添加一些其他数据结构。您可能想看看min堆(例如,在Go中,有)。谢谢!但使用map是必要的,因为我需要通过元素的ID随机访问元素。有什么想法吗?所以在sketch中,我要做的是(如果您愿意,我可以更详细地写出来作为答案):有一个数据结构,它既有map(值)又有堆(过期时间)。每次向映射中添加一个条目时,它都会被插入到堆中,并在其过期时间设置密钥。每次有人执行某种映射访问操作时,首先从堆中弹出所有过期的元素(基本上,一直到过期时间超过time.Now()
),然后从映射中删除所有元素。然后执行所请求的任何操作。或者,如果您希望映射项在没有人使用映射的情况下过期(这可能有助于回收程序其他部分使用的内存),您可以运行单独的goroutine,该goroutine只循环从堆中弹出内容,直到它们过期为止,并从映射中删除元素(您需要确保堆和映射都已正确同步-可能使用sync.RWMutex
或类似的方法;这是使用通道进行同步的一种情况。拥有10个映射,并在所有映射中查找元素。每一分钟都要扔掉最旧的地图,再做一张新的。那看起来就像我在寻找的一样!谢谢,谢谢你的密码。几天前我想知道这个问题,我也想到了类似的解决方案。每秒扫描整个地图可能太多了,但我想这取决于应用程序。另一个解决方案是保留地图和链接列表。链表包含成对的(键、时间戳)。你从后面推,从前面拿,所以按键是按时间排序的,很快就能知道哪些按键可以被移除。如果你设置了正确的睡眠时间,甚至可能是瞬间的。这会占用更多内存,但速度会更快。@matvey.co如果答案对您有帮助,您可以通过点击向上投票/向下投票按钮下的小复选标记将其标记为正确答案。siritinga这在很大程度上取决于地图上有多少个项目以及每秒有多少个get/set。@其中一个是对的。我知道他需要插入后10分钟的数据,而不是上次访问时的数据。我想这可以从两个方面来解释。如果已知元素的最大数量,那么使用切片的循环缓冲区当然比前面建议的a列表或堆要好。我想知道为什么存储库是“存档”的?简而言之(除了其他原因),在较大的社区中有更好的替代方案(以buntdb为例)。