一、主要內容
概要
作者認爲內存中鍵值存儲(In-memory key-value stores)的熱點問題被忽視了,並提出了一種名爲HotRing的熱點可感知的KV數據結構,它具有以下的特性:
- 基於有序環哈希索引結構,通過讓頭節點更靠近熱點數據來提高熱點數據的訪問速度
- 提供輕量、運行時的熱點轉移檢測策略
- 支持併發且無鎖
1介紹
互聯網公司在實際生產環境中廣泛應用內存中鍵值存儲來提高數據存儲的性能。學者們研究了不同場景下的熱點問題,並且在一些場景中提出了有效的解決方案。然而,內存中鍵值存儲場景下的熱點問題被忽略了,但這個問題在互聯網時代的變得空前重要。目前很多數據結構實現的KVS都不能感知熱點項目,基於哈希表的KVS如果希望能提高熱點項目的訪問性能,會造成很大的開銷。所以作者提出了基於有序環哈希索引結構的熱點可感知KV數據結構HotRing。
2背景與動機
2.1哈希索引與熱點問題
在KVS中,哈希索引是最常用的結構,一般哈希索引包含一個哈希表和一個衝突鏈。
哈希索引無法感知熱點項目,所以熱點項目是平均地分佈在衝突鏈中,在工作負載高度傾斜的情況下,這種分佈會導致整體性能的嚴重下降。
針對熱點問題,目前有兩種解決辦法:
- 使用CPU cache存儲熱點項目,但cache容量很小,只能存儲全部容量的0.012%。
- rehash,但會成倍地增加內存消耗,而性能提升有限,不划算。
2.2理論證明熱點感知的好處
通過公式:
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲\begin{split} …
證明了採取熱點感知的數據結構的性能提升,代碼驗證詳見formula1.cpp
, formula2.cpp
, formula3.cpp
。
2.3挑戰與設計原則
在設計解決方案時,需要滿足以下兩個條件:
-
需要輕量級的熱點感知策略,並且底層數據結構要支持熱點轉移
- 將衝突鏈表改成環,以支持熱點轉移且可以訪問到所有項目
-
需要支持大規模併發
- 採用無鎖結構實現刪除和插入操作
- 實現熱點移動檢測,頭部指針移動和重新哈希等基礎操作
3HotRing設計
3.1有序環
作者對於有序環的設計滿足以下三個條件:
①將衝突鏈表首尾相接,變成環
②哈希表中的頭指針可以指向環中任意項
③環中各項有序排列
這種設計便於以任意節點作爲遍歷操作的首項,都可以遍歷全部的項目,便於感知到熱點項目後,將頭指針指向熱點項目。但由於併發訪問有可能改變哈希表中頭指針指向的項目(比如頭指針指向的項目被刪除),所以僅僅將頭指針指向作爲搜索停止條件是不夠的,因此作者設計了環中節點的排序規則並制定了搜索停止條件。
排序規則:
搜索命中條件:
搜索未命中(終止)條件:
原文公式:
Cannot read property 'type' of undefined
注意:假設 指向$ order_i$ ,那麼 < 時,爲第一項 爲最後一項
舉個例子:
- 項目B滿足搜索終止條件1,搜索項位於環中兩項之間,未命中
- 項目D滿足搜索命中條件,命中
- 項目G滿足搜索終止條件2,搜索項位於環中最小項之前,未命中
- 項目H滿足搜索終止條件3,搜索項位於環中最大項之後,未命中
3.2熱點轉移識別
3.2.1隨機移動
描述
將頭指針定期移動到潛在熱點,不需要記錄任何歷史元數據就可以決定。
熱點調整
- 當一個線程執行R次請求後,進行一次熱點轉移
- 移動頭指針
- 若頭指針已經熱點項目,那麼不需要移動頭指針
- 否則,移動頭指針到本次請求的項上
優點
- 實現簡單,執行策略的開銷小
缺點
- 當環中存在多個熱點項目時,只能處理一個熱點項目
- 可能會產生頭節點的頻繁移動,導致該策略會非常低效
- 工作負載傾斜較小
- 選擇的週期R較小
3.2.2統計採樣
描述
定期發起一次是否需要統計採樣的判斷,若需要採樣,則以接下來環長度個訪問作爲樣本,通過公式統計計算出新的熱點位置;否則,不做操作。
索引格式
頭指針head
格式:
active
:1 bit,控制統計採樣的標識total_counter
:15 bits,統計採樣時環總訪問次數address
:48 bits,環中某項的地址
環上next
指針格式:
rehash
:1 bit,控制rehash的標識occupied
:1 bit,控制併發,保證併發訪問的正確性counter
:14 bits,某一項的具體訪問次數address
:48 bits,後一項的地址
統計採樣
-
當一個線程執行R次請求後,進行一次熱點轉移
-
移動頭指針
- 若頭指針已經熱點項目,那麼不需要移動頭指針
- 否則,開啓統計採樣,採集環長度個樣本,根據採樣結果,移動頭指針
head.active
置位- 統計接下來環長度個訪問,對每項的訪問次數與總訪問次數進行計數
熱點調整
- 在統計採樣中,最後一個進行訪問線程負責計算頻率計算和熱點調整
- 關閉
head.active
- 遍歷環,計算每一項的訪問頻率
- 根據公式6計算頭指針指向每一項時,將最小的一項t作爲新的熱點項目
- 若熱點項目不同,則使用CAS操作設置頭指針
- 重置所有計數器
- 關閉
RCU寫入密集型熱點
當進行大量修改操作時,需要使用RCU協議來保證性能,HotRing採用了這一協議。RCU是指read-copy-update,讀-拷貝修改。當進行這RCU操作時,需要修改前一項的next
指針,因此需要遍歷整個環來取得被操作項的前一項。這就導致若對某項進行頻繁的RCU操作,也會導致該被操作項的前一項訪問頻繁,而且每次伴隨着開銷較大的遍歷操作。基於此,當進行RCU操作時,會把統計採樣的訪問次數加到被操作項的前一項上。
同時,當發生小於8字節的修改時,HotRing提供了原地更新算法。
熱點繼承
當對頭節點執行更新或刪除時,需要一種機制來保證頭節點儘量指向一個hot item,來保證性能。
- 若環只有一項,直接通過CAS更新頭指針即可
- 若環有多個項,利用已有的熱點信息(即頭指針的位置)處理:
- RCU更新:指向新版本的頭(因爲很可能會被再次訪問,比如讀取)
- 刪除:指向被刪除項的下一個節點
優點
- 適用於環中存在多個熱點項目的情景
- 選出的hot item可以提供更可靠
缺點
- 實現相對複雜,採樣統計開銷比較大
3.3併發操作
頭指針的移動導致了併發操作的兩個問題:
- 頭指針移動和其它操作併發,需要檢查指針的有效性
- 更新或刪除時,需要檢查頭指針是否指向被刪除項,並正確地更改頭指針
讀取
- 不需要額外操作,過程完成無鎖。
插入/更新/刪除的問題:
a):一線程通過RCU更新B項,同時另一線程需要在BD之間插入C。若不做處理,則兩個操作都會成功,最終導致舊B項指向了C項,因此造成C項丟失。所以,RCU更新操作前需要將舊B項的occupied
設置爲佔用;插入操作前檢查需要修改的next
指針是否被佔用,保證插入操作檢測到被佔用,從而進行重試。
b):一線程通過RCU更新B項,同時另一線程通過RCU更新D項。若不做處理,則兩個操作都會成功,最終導致舊B項指向了D‘,B’項指向了舊D項。解決方法如a)
c):一線程進行刪除B項操作,同時另一線程通過RCU操作更新D項。若不做處理,則兩個操作都會成功,最終導致舊B項指向了D‘,A項指向了舊D項。所以,刪除操作前,需要把刪除項的occupied
設置爲佔用,更新操作解決方法如a)。保證另一線程的RCU更新操作失敗,並重新嘗試操作。
插入
- 創建新項
- 連接新項的
next
指針 - 檢查前一項的
next
指針是否被佔用- 若沒有,則通過CAS修改前一項的
next
指針 - 若被佔用,則重新嘗試兩步
- 若沒有,則通過CAS修改前一項的
更新
當更新的數據不超過8字節:使用in-place CAS,不需要其它操作。
當更新的數據超過8字節:使用RCU更新:
- 設置舊項的
occupied
爲佔用 - 再更改前一項的
next
指針
刪除
- 設置被刪除項的
occupied
爲佔用 - 再更改前一項的
next
指針
頭指針移動
- 正常的頭指針移動
- 設置新指向的項目的
occupied
爲佔用
- 設置新指向的項目的
- 更新/刪除頭指針指向的項目
- 更新時,需要將舊版本項目的
occupied
爲佔用 - 刪除時,需要將被刪除項目和被刪除項目的下一項的
occupied
爲佔用
- 更新時,需要將舊版本項目的
3.4無鎖rehash
HotRing支持無鎖rehash操作。而和其它使用負載因子來觸發rehash不同,HotRing使用訪問開銷(即操作平均內存訪問次數)來觸發rehash。整個rehash操作分爲初始化,分割,刪除三步。
初始化
rehash線程初始化一個2倍大小的哈希表,原表中的每個環都將被拆成兩個環。rehash操作中,哈希值用於定位的部分從k位變成了k+1,tag則減少了1位,因此根據tag的值分成[0, T/2)和[T/2, T)兩個部分。
rehash線程會創建一個rehash node
,其中包含兩個rehash child item
,這兩個item
不包含KV結構,tag
分別爲0和T/2,並用rehash標誌來標識。它們將作爲拆分後兩個環的頭節點。
分割
rehash線程將rehash node
中的兩個rehash child item
插入到環中,此時新表被激活,後續的訪問可以通過新表進行,而過去舊錶的訪問也依然可以繼續進行。
刪除
保持一段過渡期,所有針對舊錶的訪問結束後,rehash線程刪除rehash nodes
,將環真正拆成兩個新環。
二、主要貢獻
- 作者證實了現有內存索引中的熱點問題,並證明了熱點感知設計具有極大提高熱點項目的性能的潛力。
- 作者提出了有序環哈希結構HotRing,它的主要貢獻如下:
- HotRing通過將頭指針移近熱點項目,來快速訪問熱點項目。
- HotRing採用輕量級策略在運行時檢測熱點轉移。
- HotRing是利用熱點感知進行設計的第一次努力。
- 作者通過無鎖設計令HotRing支持高併發訪問,並設計了針對HotRing的特定操作:
- 熱點移位檢測
- 頭部指針移動
- 有序環重新哈希
- 作者基於實際工作量基準的方法評估HotRing的性能,結果表明:當訪問高度不對稱時,HotRing的性能明顯優於其他KVS。
三、論文的長處和短處
長處
通過較小的改動實現了性能的大幅提升
作者僅通過將哈希索引結構中的衝突鏈表結構修改成環結構,既不影響原有的查找功能,又可以在此基礎上增加熱點轉移功能。這提醒我在今後的研究中,可以尋找系統中未達成最優結構的小組件來進行優化,進而提高系統的整體性能。
Rehash操作保證了系統併發性能的穩定
作者將Rehash操作分成Initialization,Split,Deletion三步:
- 第一步僅僅申請新哈希表與創建Rehash Node, 並將新表中的頭指針指向Rehash Node中相應的Rehash Item,這個過程中不會影響系統的其他操作;
- 第二步將Rehash Node中兩個Rehash Item插入到舊的環中,這個操作相當於在環結構中進行了兩次插入操作,不會影響其他正在進行的操作。當操作完成後,針對舊哈希表的訪問不會受到影響,同時,新哈希表也可以開始工作。
- 第三步,等待針對舊哈希表的請求全部結束後(這個期間僅僅阻塞rehash線程,其他線程不受影響),將執行刪除Rehash Node的操作,這個過程基本相當於執行了兩次刪除節點操作,並重新指定頭節點。
綜上,在Rehash過程中,不存在一個時間點(段)需要拒絕所有請求,這保證了系統性能的穩定可靠。而且作者用較小的開銷就實現了Rehash功能,非常值得學習。
研究方向新穎
論文基於實際工業環境下工作負載高度不平衡的問題,發現目前主流的KVSes針對內存中的熱點問題沒有高效的解決方案。本文揭示了該問題對目前工作的嚴重影響。
研究邏輯縝密
論文主體結構爲:
-
發現問題->
-
證明問題嚴重性->
-
提出可能的解決方案->
-
實現解決方案->
-
證明解決方案的有效性
研究邏輯縝密,研究過程層層遞進,結果令人信服。
解決方案具備參考價值
論文中提出,在互聯網時代,社會熱點問題會在較短時間內被大量訪問。並且社會熱點會隨着時間轉移。所以,具備熱點感知能力的存儲系統有比較好的發展潛力,只給未來相關領域的研究提供了新的解決思路。
短處
Rehash操作中激活新表描述的不清楚
根據原文,Rehash操作在插入Rehash Item後,新表被激活,此時來自新表的訪問通過比較tag來選擇頭節點,來自舊錶的訪問通過識別Rehash Node繼續執行。
-
前半句:當新表產生後,可以直接通過計算哈希值來確定New Head,而此處說新表需要通過比較tag來確定新的頭節點,表述有些模糊。
-
後半句:結合前文所述,結構採用比較order的方式來確定項目,其中 ,此處所表達的意思可能是:通過識別Rehash Node確保不會產生比較錯誤。
如果此處能結合示意圖進行舉例說明,可以更清楚地表達作者的想法。
在測試部分介紹YCSB工作負載時,描述不夠直觀
在文中只對ABCDF的工作負載情況進行了文字介紹。如果可以把每種工作負載的情況繪製成表格形式,那麼可以有利於讀者更直觀地瞭解工作負載的配置情況與比較不同工作負載情況下的性能表現。
研究視角有侷限
論文核心問題是在工作負載高度不平衡的環境下提出的,研究視角主要聚焦於實際生產環境中產生的問題。作者在測試部分着重比較了在工作負載不平衡的情況下各種KVSes的比較,還可以分析比較一下工作負載相對平衡的環境下的性能表現。
四、收穫
專業知識方面
CAS
CAS指Compare and Swap,其思想是利用三個參數:一個當前內存值V,舊的預期值A,即將更新的值B實現一種無鎖且保證線程安全的機制。它的操作過程如下:
- 線程拷貝需要修改的內存中的值保存爲A
- 線程得到即將更新的新值爲B
- 線程讀取需要修改的內存中的當前值V,比較V和A是否相等
- 相等,說明該值沒有被其他線程改動,將內存中的值改爲新值,修改成功
- 不相等,說明該值已經被別線程改動過,修改失敗
比較修改操作需要保證是原子操作,一般是由CMPXCHG的彙編指令來實現,上層應用也可以通過加鎖操作等來保證。(不過這喪失了無鎖化的初衷)
ABA問題指在進行CAS操作時,內存中的值由A改成B又被改回A,而CAS線程只保存了第一個版本的內存值A,並不知道內存值已經被變動過。ABA問題一般可以通過給變量增加版本號來解決,將A->B->A的過程變成A1->B2->A3,那麼進行CAS操作時既時發生ABA問題,線程也會察覺到,並保證不會進行修改操作。
RCU
RCU是指Read-Copy-Update,是一種針對讀取請求多,修改請求少環境下的鎖結構,它的操作過程如下:
- 讀取線程不需要獲得任何鎖就可以訪問數據
- 修改線程在訪問它時首先拷貝一個副本
- 然後對副本進行修改
- 當其他線程都退出對數據的操作後,再將原來數據的指針指向修改後的新數據
- 釋放存儲舊數據的空間
使用RCU機制時,讀取線程不需要獲取鎖資源,所以在讀取請求較多時,使用RCU可以有效提高系統性能。但RCU需要其他的鎖機制來實現修改線程間的同步,並且修改線程還需要複製數據內容,延遲釋放空間等。所以當修改頻繁時,RCU機制的性能不是很好。
HotRing數據結構
HotRing是一種具備熱點感知能力的有序環哈希結構,它大大改善了工作負載高度不平衡環境下的存儲系統性能。
研究常識方面
工作負載不平衡
認識到工業界實際工作負載非常不平衡,一般狀況下,超過一半(57.3%)的訪問請求只會訪問1%左右的數據,而極端狀況下,超過九成(91.7%)的訪問請求只會訪問1%左右的數據。所以熱點問題在各種存儲系統中都是一個非常嚴重的性能瓶頸。
熱點問題分佈廣泛
在存儲領域,熱點問題分佈非常廣泛。來到互聯網時代後,熱點問題更加嚴重,當一個社會熱點事件出現時,大量的訪問請求會集中在極少的數據上。
熱點感知方案具有潛力
具備熱點感知能力的存儲系統可以更好的應對當前的互聯網環境,同時,熱點感知在處理熱點問題上有很好的潛力,可以嘗試應用在其他場景下。
五、論文存疑的地方
疑問1
在論文中,Rehash操作的第三步,Rehash線程會在過渡期等待所有針對舊哈希表的訪問結束後開始分裂。並且,在過渡期中,HotRing僅僅阻塞了Rehash線程。但我考慮,這還不能保證Rehash操作的正確性。
Rehash操作中,如果存在針對新表的RCU操作,RCU線程若還未找到被更新項的前一項,舊環就被斷開了,可能造成該RCU線程死循環。
六、實踐
設計目標:
- 根據論文思想,復現論文方法,設計編寫符合HotRing思想的數據結構。
- 通過對比實驗,體現HotRing相比HashTable的優點。
測試指標:
指標 | 說明 |
---|---|
總查找次數(findcnt) | 反應系統完成任務的總開銷 |
單次最高查找次數(maxFindcnt) | 一定程度上反應系統的尾延遲 |
單次最低查找次數(minFindcnt) | 反應系統單次查詢的最優表現 |
平均單詞查找次數(averageFindcnt) | 反應系統每次查詢操作的 |
消耗的時間(useTime) | 反應系統完成任務的總耗時 |
測試數據:
控制單一變量:theta
數據文件 | 總項數 | 總查詢數 | 鍵桶比 | theta | 備註 |
---|---|---|---|---|---|
test1.data | 2000 | 10’000’000 | 2 | 0 | 工作負載分佈完全平均 |
test2.data | 2000 | 10’000’000 | 2 | 1 | 工作負載分佈不平衡 |
test3.data | 2000 | 10’000’000 | 2 | 2 | 工作負載分佈極度不平衡 |
控制單一變量:鍵桶比
數據文件 | 總項數 | 總查詢數 | 鍵桶比 | theta | 備註 |
---|---|---|---|---|---|
test2.data | 2000 | 10’000’000 | 2 | 1 | 平均鏈長爲2 |
test4.data | 4000 | 10’000’000 | 4 | 1 | 平均鏈長爲4 |
test5.data | 8000 | 10’000’000 | 8 | 1 | 平均鏈長爲8 |
測試結果:
數據文件 | 數據結構 | findcnt | maxFindcnt | minFindcnt | averageFindcnt | useTime |
---|---|---|---|---|---|---|
test1.data | HashTable | 16538091 | 10 | 1 | 1.65381 | 76s |
test1.data | HotRing | 16531911 | 9 | 1 | 1.65319 | 116s |
test2.data | HashTable | 22456351 | 6 | 1 | 2.24564 | 96s |
test2.data | HotRing | 10816535 | 5 | 1 | 1.08165 | 84s |
test3.data | HashTable | 22009405 | 8 | 1 | 2.20094 | 93s |
test3.data | HotRing | 10003612 | 7 | 1 | 1.00036 | 77s |
數據文件 | 數據結構 | findcnt | maxFindcnt | minFindcnt | averageFindcnt | useTime |
---|---|---|---|---|---|---|
test2.data | HashTable | 22456351 | 6 | 1 | 2.24564 | 96s |
test2.data | HotRing | 10816535 | 5 | 1 | 1.08165 | 84s |
test4.data | HashTable | 23549070 | 8 | 1 | 2.35491 | 99s |
test4.data | HotRing | 11057968 | 7 | 1 | 1.1058 | 83s |
test5.data | HashTable | 22783760 | 7 | 1 | 2.27838 | 96s |
test5.data | HotRing | 10882926 | 6 | 1 | 1.08829 | 83s |
測試評價
- 從兩組實驗中findcnt指標與averageFindcnt可以明顯看出:當工作負載相對平衡時,HotRing與HashTable的總查詢次數沒有明顯差距;但在工作負載極度不平衡下,hotring的總查找此處遠遠低於HashTable,本次測試僅僅測試了量級下的查詢性能比較。當測試量級更大時,這種差距應該會更加明顯。
- 根據
test1.data
可知,由於HotRing的search操作需要更多的判斷條件與處理,所以當查找次數差不多時,HotRing的時間開銷是要大於HashTable的。(此處與HotRing功能的具體實現方式也有很大關係) - 根據鍵桶比這一組測試結果發現:隨着鍵桶比的提升,對HashTable與HotRing的性能影響不是非常明顯,與論文中的測試結果有明顯差距,推測可能的原因是:測試數據的量級比較小(級),干擾因素造成的影響較大,偶然性也比較大。
- 通過maxFindcnt來反應尾延遲的效果很差,原因我認爲有以下幾點:
- maxFindcnt只能反應最差的一次查詢操作所需要的查詢次數,所以偶然性非常大,單次異常數據會直接導致指標失效。
- 測試尾延遲更應該關注各次查詢開銷(本實驗中爲查找次數)的分佈情況,單獨一次操作產生的數據指標,意義不大。
- minFindcnt指標基本無用,在大規模數據測試下,單獨一次操作產生的數據指標統計意義很小。
問題與解決:
HotRing耗時遠高於HashTable
原因:在編寫HotRing的search函數時,調用了new與delete,導致開銷很大,嚴重影響了HotRing的性能。
解決:在HotRing結構體內部定義了一個用於比較的htEntry,查詢操作中直接對該變量賦值即可,避免頻繁new與delete。
當鍵桶比變大時,HashTable的性能有明顯改善,甚至略優於HotRing
原因:在隨機數據程序中,取key值時,用於隨機數取餘的MOD值爲項數的十倍,由於總項數擴大了,所以隨機數取值範圍也擴大了,這有可能對hashfunction有影響。
解決:將MOD值固定爲20000。
測試截圖:
test1.data:
test2.data:
test3.data:
test4.data:
test5.data:
代碼:
隨機數據生成:radomdata.cpp
數據文件:test1.data
, test2.data
, test3.data
, test4.data
, test5.data
測試程序:hotring-r.h
, hotring-r.cpp
, hashTable.cpp
, main.cpp
七、未來研究設想
設計熱點感知算法
作者在論文中提出了兩種輕量級的熱點感知算法,未來可以設計更多的熱點感知算法來應對不同的應用場景,比如:
- 可以嘗試設計熱點感知算法來提前預測熱點的來臨
優化數據結構
可以在作者設計的HotRing結構基礎上繼續優化該數據結構,比如:
- 加入前向指針,令HotRing面對RCU操作,刪除操作時,可以迅速確定前一項的位置,並且可以解決疑問1。
運用熱點感知方案到其他場景
熱點感知方案的核心思想是:**找到熱點信息,將熱點信息放在系統中可以較快訪問到的位置。**那麼,我們可以將統計採樣策略移植到傳統的關係型數據庫中,或者移植到計算機內存與CPU之間,設計獨立的模塊,在CPU訪問Cache之前,通過統計採樣,爲CPU提供可能需要被訪問的數據。
參考
keys961 Blog 論文閱讀-HotRing: A Hotspot-Aware In-Memory Key-Value Store
Glitter試做一號機 HotRing: A Hotspot-Aware In-Memory Key-Value Store
FAST '20 - HotRing: A Hotspot-Aware In-Memory Key-Value Store講解視頻
《C++ Primer 5th》
《Redis設計與實現》
代碼及數據
csdn :https://download.csdn.net/download/qq_40758751/12507429
github :https://github.com/Focus5679/HotRing-simple-and-simple-demo