什么数据结构可以用来支持外键约束

时间:2018-12-12 15:58:31

标签: dictionary go data-structures tree big-o

提供一些上下文,我正在使用gRPC在每一个客户端连接的服务器上实现扇出(流式传输),我将流保存到映射中,当客户端断开连接时,我只是将其从映射中删除,我可以仅使用“实时”客户端/连接来更新地图。

因此初始地图如下所示:

key        value
client1    stream
client2    stream
client3    stream

此实现的问题在于,如果客户端多次连接,则只有最后一个客户端(使用相同的key id)会收到“广播”消息,因为该映射将使用最新的覆盖现有的流一个来自客户端连接,因此我需要使用唯一键或找到一种方法来匹配,关联客户端和连接。

我发现只需切换地图并使用连接ID作为键即可,就像这样

conn_id   client_id
conn1     client1
conn2     client2 \
conn3     client2  > 3 connections for client ID 2
conn4     client2 /

这有效并且有助于向所有连接的客户端发送消息,但是如果我想向特定客户端发送消息,则事情变得更加复杂,因为我需要范围/遍历完整映射以将值与想要的客户。

为解决此问题,我使用client_id切换回了地图,但我没有使用流作为值,而是使用了切片列表

key        value
client1   [stream, stream, stream]
client2   [stream]
client3   [stream, stream] 

问题仍然存在,因为为了向所有客户端发送消息,整个地图仍然需要遍历/范围以获取每个客户端的流并将消息发送给每个客户端。

因此,在不使用数据库的情况下,不知道遵循big O notation的最佳做法可以使用哪种数据结构?

主要是想找到行为与数据库foreign keys类似的东西,可能有2个映射,一个映射保留所有连接,而另一个仅保留每个客户端的状态或连接数,例如:

key      value            client_id   # streams 
conn1    client1          client1     1   
conn2    client2          client2     3
conn3    client2
conn4    client2

但是,当clients映射中不再有来自该客户端的连接时,如何从connections映射中“以原子方式”删除该客户端。

这是一个尝试,但想知道是否可以改进:

https://play.golang.org/p/VEoLsWh6dbJ

package main

import (
    "fmt"
    "sync"
)

func main() {

    clients := &sync.Map{}
    for i := 0; i < 100; i++ {
        clientID := fmt.Sprintf("client-%d", i%5)
        connID := fmt.Sprintf("conn-%d", i)
        client, ok := clients.LoadOrStore(clientID, connID)
        if ok {
            switch c := client.(type) {
            case string:
                client = []string{c, connID}
            case []string:
                client = append(client.([]string), connID)
            }
            clients.Store(clientID, client)
        }
    }

    list := func(k, v interface{}) bool {
        fmt.Printf("k: %+v v: %v\n", k, v)
        return true
    }

    clients.Range(list)

    // Get all connections
    broadcast := func(k, v interface{}) bool {
        fmt.Printf("To all connections from= %q\n", k)
        for _, v := range v.([]string) {
            fmt.Printf("msg to conn = %s\n", v)
        }
        return true
    }
    clients.Range(broadcast)
}

在这里,我尝试仅使用地图(易于处理删除)的速度较慢,但​​分配的字节数较少:

https://play.golang.org/p/hz5uRX-VAvH

package main

import (
    "fmt"
    "sync"
)

func main() {
    clients := &sync.Map{}
    for i := 0; i < 100; i++ {
        clientID := fmt.Sprintf("client-%d", i%5)
        connID := fmt.Sprintf("conn-%d", i)
        conns, ok := clients.Load(clientID)
        if ok {
            conns.(*sync.Map).Store(connID, i)
        } else {
            conns := &sync.Map{}
            conns.Store(connID, i)
            clients.Store(clientID, conns)
        }
    }

    list := func(k, v interface{}) bool {
        fmt.Printf("k: %v\n", k)
        listValue := func(j, l interface{}) bool {
            fmt.Printf("  j = %+v\n", j)
            return true
        }
        v.(*sync.Map).Range(listValue)
        return true
    }

    clients.Range(list)

    // Get all connections
    broadcast := func(k, v interface{}) bool {
        fmt.Printf("To all connections from= %q\n", k)
        listValue := func(j, l interface{}) bool {
            fmt.Printf("  msg to conn = %s -- %v\n", j, l)
            return true
        }
        v.(*sync.Map).Range(listValue)
        return true
    }
    clients.Range(broadcast)
}

0 个答案:

没有答案