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
    功能
  • 可以设置一个回调(可选),通知哪个键和值超时
  • 超时可以是显式的,也可以是滑动的

我建议使用golang的内置包Map
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为例)。