2017-03-01 56 views
1

對於每個客戶端,服務器都會爲該特定客戶端創建一個會話。會話的過期時間爲1天。那最終會有多達數十億的會話。存儲大量會話的最有效的數據結構是什麼?

假設我使用哈希映射,然後當客戶端與服務器通信時查找速度會很快。但是,我需要清除那些過期的會話,例如每小時一次。在擦除期間,由於數量龐大,可能需要一些時間,這將導致服務器無法處理來自客戶端的通信。

那麼有沒有高性能的解決方案呢?即我不想鎖定地圖以擦除過期的地圖。

+0

_「我不想鎖定地圖以擦除過期的地圖。」_您可以將需要擦除的散列存儲在單獨的容器中,並僅通過鎖定地圖來進行單獨的rease操作。 –

+0

這是一個超出C++編程問題範圍的非常廣泛的體系結構問題。 –

+0

會話狀態有多大?數十億美元似乎需要外部存儲器,而且經常與適用於RAM的性能權衡和算法不同。 – doynax

回答

1

我不認爲地圖真的是最好的收藏。根據你的想法,我會選擇一個套餐(如果你不需要訂單,那麼就是一個無序套餐)。因爲你永遠不會有兩次相同的會話,他們將會不同,並且你不需要地圖提供的關聯,或者我沒有正確理解你的問題。

2

如果您的會話數量非常大,那麼使用數據結構可能太簡單了,您將需要稍微不同的方法。

查看將會話數據存儲在Redis或其他鍵值存儲中。對於高負載的服務器來說這將更加正常。 Redis和大多數其他人提供持久性,如果您需要在後臺清除事務,則不會出現鎖定問題。

1

簡單的解決方案:使用散列表。當您在存儲區中搜索條目時,請刪除您遇到的任何過期會話。這幾乎是免費的,因爲無論如何你都在搜索這個鏈條。它不能保證會話在到期時立即被刪除,但很可能包含過期會話的鏈將在不久後被搜索。

您應該將散列表預設爲固定數量的存儲桶,表示您希望成爲服務器的容量。這樣就避免了重複使用的需要,這意味着每個桶鏈都可以獨立鎖定。儘管如此,你不需要爲每個鏈條鎖定鎖定。你可以對同一個鎖使用幾個甚至多個鏈。選擇足夠多的鎖,以便在峯值請求壓力下預期的鎖爭用將會很低;您可以根據您擁有的同時處於活動狀態的處理程序線程的數量計算出一個好的數字如果鏈是內存駐留的,鏈搜索將花費很少的時間,所以它幾乎總是在上下文切換之前完成。因此「同時活動」意味着它們實際上映射到CPU並運行,而不僅僅映射到內核進程。因此,即使只有一小撮鎖,您也應該能夠將存儲鏈爭用減少到非常低的水平。

-1

解決這個問題的一種方法是創建一個哈希映射來保存會話和一個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 

如果你擔心清理過期會話將鎖定您的哈希表的時間過長,設定上限您將在任何時候刪除過期會話的數量。如果您在添加新會話時將其設置爲僅清理兩個過期會話,那麼您將最大限度地減少數據結構被鎖定的時間,並且過期的會話不會拖延太久。

+0

Downvoter?習慣於爲downvote提供一個原因。 –

相關問題