存储大量会话的最有效数据结构是什么?

时间:2017-03-01 15:24:13

标签: c++ algorithm dictionary data-structures

对于每个客户端,服务器都会为该特定客户端创建会话。会话的到期时间为1天。因此,最终会有数十亿次会议。

假设我使用哈希映射,那么当客户端与服务器通信时,查找会很快。但是,我需要删除那些过期的会话,例如每小时一次。在擦除期间,由于数量巨大可能需要一些时间,这将导致服务器无法处理来自客户端的通信。

那么有没有针对此的高性能解决方案?即我不想锁定地图以删除过期的地图。

4 个答案:

答案 0 :(得分:2)

如果您的会话数量非常多,使用数据结构可能过于简单,您需要稍微不同的方法。

查看在Redis或其他键值存储中存储会话数据。对于高负载的服务器,这将更为正常。 Redis和其他大多数人提供持久性,如果你需要在后台解决问题,就不会有锁定问题。

答案 1 :(得分:1)

我不认为地图真的是最好的收藏品。根据你的想法,我会选择一个Set(如果你不需要订单,那就是无序的)。因为你永远不会有2次相同的会话,所以他们都会有所不同,你真的不需要地图提供的关联,或者我没有正确理解你的问题。

答案 2 :(得分:1)

简单的解决方案:使用哈希表。当您在桶中搜索条目时,请删除您遇到的所有过期会话。这几乎是免费的,因为无论如何你正在搜索链条。它并不保证在到期时会立即删除会话,但很可能不久之后会搜索包含过期会话的链。

您应该将哈希表预设为固定数量的桶,这些桶代表您期望的服务器容量。这避免了重新散列的需要,这意味着每个存储桶链可以独立锁定。但是,你并不需要锁定每个链条;你可以为几个 - 甚至很多 - 链使用相同的锁。选择足够数量的锁,以确保在峰值请求压力下您的预期锁争用率较低;您可以根据您拥有的同时活动的处理程序线程数来计算一个好的数字。如果链是内存驻留的,链搜索将花费很少的时间,因此它几乎总是在上下文切换之前完成。所以"同时活跃"意味着它们实际上映射到CPU并运行,而不仅仅映射到内核进程。因此,即使是一小部分锁,您也应该能够将桶链争用降低到非常低的水平。

答案 3 :(得分:0)

处理此问题的一种方法是创建一个哈希映射来保存会话,以及一个MRU(最近使用的)列表。 MRU列表实现为双向链表。每当用户访问该站点时,他的会话将被移回MRU列表的顶部。此外,每当创建会话时,系统都会检查MRU列表中的最后一项,以查看最早的会话是否已过期,以便您可以将其删除。

或者,您可以删除列表末尾的所有过期会话。

此外,如果您的查找代码尚未被删除,则您希望删除已过期的会话。

因此,当您收到请求时,事件序列如下所示:

session = get session info from user token
if no session
    create session
    add to front of MRU list
else if session has expired
    delete from mru list
    remove from hash map
else // session has not expired
    move session to front of MRU list
end

// delete expired sessions
p = last item in MRU list
while p has expired
    prev = p->prev
    remove from MRU list
    delete from hash map
    p = prev
end

如果您担心清理已过期的会话会将哈希映射锁定的时间过长,请对您在任何时候删除的过期会话数设置限制。如果您在添加新会话时将其设置为仅清理两个已过期的会话,则可以最大程度地减少数据结构锁定的时间,并且过期的会话不会停留太久。