在Go语言中使用TTL选项的映射

huangapple go评论139阅读模式
英文:

Map with TTL option in Go

问题

我需要构建一个像这样的数据结构:

map[string]SomeType

但它必须在大约10分钟后从内存中清除存储的值。
第二个条件是记录的数量 - 它必须很大。这个数据结构必须每秒至少添加2-5K条记录。

那么,在Go语言中,最正确的方法是什么?

我正在尝试为每个新元素创建一个带有超时的goroutine。还有一个(或多个)垃圾收集器goroutine,带有通道来接收超时并清除元素。
但我不确定这是否是最清晰的方法。拥有数百万个带有超时的等待goroutine是否可以接受?

谢谢。

英文:

I need to build a data-structure like this:

map[string]SomeType 

But it must store values for about 10 minutes and then clear it from memory.
Second condition is records amount - it must be huge. This data-structure must add at least 2-5K records per second.

So, what is the most correct way in Go to make it?

I'm trying to make goroutine with timeout for each new elemnt. And one(or more) garbage-collector goroutine with channel to receive timeouts and clear elements.
But I'm not sure it's the most clear way. Is it Ok to have millions of waiting goroutines with timeouts?

Thanks.

答案1

得分: 41

你需要创建一个结构体来保存你的映射,并提供自定义的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
}

playground

注意(2020-09-23):由于某种原因,当前版本的playground上的时间分辨率偏差很大,但这个代码是正常工作的。如果要在playground上尝试,请将休眠时间更改为3-5秒。

英文:

You will have to create a struct to hold your map and provide custom get/put/delete funcs to access it.

Note that 2-5k accesses per second is not really that much at all, so you don't have to worry about that.

Here's a simple implementation:

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
}

<kbd>playground</kbd>

note(2020-09-23): for some reason the time resolution on the current version of the playground is way off, this works fine, however to try on the playground you have to change the sleep to 3-5 seconds.

答案2

得分: 5

请看一下buntdb

> tinykv不再维护。

只是为了记录,我遇到了同样的问题,并编写了tinykv包,它在内部使用了一个映射。

  • 它使用time.Time的堆来处理超时,因此不会遍历整个映射。

  • 在创建实例时可以设置最大间隔。但是,用于检查超时的实际间隔可以是任何大于零且小于最大值的time.Duration值,根据最后一个超时的项目而定。

  • 它提供了CASTake功能。

  • 可以设置回调函数(可选),通知哪个键和值已超时。

  • 超时可以是显式的或滑动的。

英文:

Take a look at buntdb.

> tinykv is no longer being maintained.

Just for the record, I had the same problem and wrote tinykv package which uses a map internally.

  • It uses a heap of time.Time for timeouts, so it does not ranges over the whole map.
  • A max interval can be set when creating an instance. But actual intervals for checking the timeout can be any value of time.Duration greater than zero and less than max, based on the last item that timed out.
  • It provides CAS and Take functionality.
  • A callback (optional) can be set which notifies which key and value got timed out.
  • Timeouts can be explicit or sliding.

答案3

得分: 1

我建议使用Go语言内置包sync中的Map,它非常易于使用,并且已经处理了并发。你可以在这里找到更多信息:https://golang.org/pkg/sync/#Map

英文:

I suggest to use Map of golang's built-in package sync, it's very easy to use and already handles concurrency https://golang.org/pkg/sync/#Map

huangapple
  • 本文由 发表于 2014年8月25日 18:43:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/25484122.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定