1. 程式人生 > >海量路由表可以使用HASH表儲存嗎-HASH查詢和TRIE樹查詢

海量路由表可以使用HASH表儲存嗎-HASH查詢和TRIE樹查詢

千萬別!很多人這樣說,也包括我。
Linux核心早就把HASH路由表去掉了,現在就只剩下TRIE了,不過我還是希望就這兩種資料結構展開一些形而上的討論。

1.hash和trie/radix

hash和tire其實是可以統一在一起的。具有相同hash值的多個項具有一個共同的特徵,這個特徵怎麼提取呢?無疑這就是hash函式的工作。而trie樹(或者radix樹,管它呢)的一棵子樹也有共同的特徵,這個特徵怎麼提取呢?無疑這就是該子樹根節點的父節點指示的某些bits在這棵子樹的每一個節點都具有相同的值。
       實際上,trie樹就是hash的一種特殊形式,其hash函式為:取某些bits
trie_hash(value, level)
{
    return value & level.bits;
}

那麼,這麼看來,子樹的所有節點都應處在一個“衝突連結串列”裡面了...trie樹的做法就是“再次hash”,hash函式隨之改變,變成取level.bits更低的某些bits了。如此看來,hash路由表解決海量路由項情況下衝突連結串列變長的方案就是再次hash了,hash函式變成什麼呢?我們後面再談。

2.TCAM的hash

TCAM在很多地方被用到,它用來根據內容查索引,常被用於路由查詢,CPU Cache查詢等,以CPU Cache為例,輸入TCAM的內容就是一個記憶體地址,而輸出的結果是一個索引,cache匹配的過程就是取到索引指示的cache line,然後比較輸入內容(地址)和該cache line指示的地址是否一致,一致就是命中。
       那麼TCAM中最核心的過程就是根據地址得到索引的過程,一般的做法就是hash,由於硬連線實現,hash函式絕對不能有太多的計算,因此一般的做法就是“取地址某些bits”,比如取4到7位一共4位,將一個32位(32位系統,實體地址索引cache為例為例)的慢速實體記憶體地址對映到4位快速cache索引,形成一個金字塔儲存結構。32位到4位的對映,丟失了的28位會形成很大可能性的衝突,而這個就是時間區域性性和空間區域性性來盡力彌補了,瞭解列維飛行的應該知道區域性性的偉大含義,它構建了我們整個人類文明。
       最簡單的hash函式就是取模,實際上也是“取某些bits”,它更加特殊,它是“取最低N bits"。

3.hash和trie樹的統一

trie樹實際上是從高位到低位逐步hash的過程構建的,其hash函式就是”取某些bits“。

4.查字典的例子-查英文和查漢字

我們小學的時候查字典一般分為音序查法和部首查法,它就形象能體現hash和trie的不同。為了簡便,我以英文單詞查法和漢字部首查法為例。
       英文單詞是嚴格一維度順序排列的,且僅有26個字母組成,因此它可以按照trie樹的方式查詢,比如what,who,where,前兩個字元都是wh,因此說它們具有這麼一個共同特徵,如果將取這個共同特徵作為hash函式,那麼在aaa,cc,sahidad,fwfwew,what,qwert,azsx,who,eee,ooo,where中查詢who,what,who,where將形成衝突連結串列,但是一步運算大大減少了匹配的數量,從11個減為3個,然後再進一步hash,按照字母順序可知at,wre,o這個順序,直接取第三個孩子節點。因此英語詞典的查詢方式非常簡便,就是一個不斷hash定位的過程,hash函式就是”取某些連續字元“。
       我們再看看漢字部首查詢法,它是一個典型的計算型hash函式的不斷hash的過程,比如在楊,林,棵,馬,牛,豬,過,皮這幾個字中查”林“字,由於漢字不是一維結構而是二維結構,它的構成是筆畫,不是排序的,因此”取某些字元“的方式完全失效(從哪個方向開始取?...怎麼算一個字元?...),因此就需要重新構造hash函數了,長期的歷史形成的漢子具有某種象形的意義,通過觀察,我們發現”木“字旁是一個特徵,這個計算過程,也就是hash函式執行過程是我們的大腦來完成的,如果說”取某些字元“更加適用於硬體實現,那麼發現偏旁部首則更加適合軟體實現,從中我們也可以分析出中國人和西方人的思維之區別。繼續往下說,發現”木字旁“之後,楊,林,棵形成了衝突連結串列,但大大減少了匹配候選字的數量,不想遍歷的話,需要再次hash,新華字典設計了筆畫數這個再hash函式,”林“字除了偏旁之外還剩下4筆畫,於是定位到了”林“,如果還衝突,那就需要遍歷了,因為商務印書館可能想不出什麼hash函數了(我不知道這種漢字部首查字法是誰發明的,就當是出版社的傑作吧...)。反過來看英文查法,總是可以最終確定性定位,因為它的不斷hash的hash函式是”取連續字元“,加之單詞長度有限且一維排列順序遞進,總是可以到最後一個字元的。
       看出區別了嗎?看出trie樹查詢和hash查詢的區別了嗎?

5.hash路由表和trie路由表

對於hash路由表查詢而言,最長字首匹配邏輯並沒有包含在hash過程中,它來自於一種冒險行為,前提是對hash函式的足夠自信。hash路由表查詢直接從32位字首hash表開始,逐步迴歸到0位字首hash表,期望在這個過程中能快速得到第一個結果,這第一個匹配結果就是最終結果。
       對於trie路由表查詢而言,最長字首匹配邏輯包含在不斷再hash的邏輯中,它匹配的是最後一個結果而不是第一個,因為”順序取某些bits“不斷hash的過程,最後匹配到的顯然是最精確的。這是和hash路由查詢的本質區別。trie查詢沒有冒險行為,它不需要冒遍歷超長衝突連結串列之險,因為老老實實地執行順序取bits這個過程總能將查詢過程引到目的地。

6.海量路由項的情況

Linux之所以用了那麼久hash路由表組織,是因為它足夠了。因為在大部分時間,路由表項數量是不多的。即便是遍歷也不會有太大的開銷,而hash的計算會大大減少遍歷的開銷,所謂的冒險最壞情況就是遍歷整個路由項,這不是為題。但是一旦遍歷整個路由表的所有路由項真的成了一個大風險的時候,或者說即使遍歷一半也吃不消的時候,用hash就不明智了。這和獅子追羚羊時的博弈類似,一個風險是一頓飯,一個風險是一條命,這是嚴格不對稱的,所以總是看到羚羊勝利(還真不能把這個當零和遊戲,因為獅子有時真的不在乎)。
       現在的問題是,如何使用hash路由表並降低風險。我們先看一下Linux自己的hash函式:
static inline u32 fn_hash(__be32 key, struct fn_zone *fz)
{
    u32 h = ntohl(key)>>(32 - fz->fz_order);
    h ^= (h>>20);
    h ^= (h>>10);
    h ^= (h>>5);
    h &= FZ_HASHMASK(fz);
    return h;
}

可見它將輸入的非0項雜湊得足夠開,但是hash的本質就是大空間往小空間對映,衝突在所難免。有人提出(比如我)在海量路由表項時將長衝突連結串列組織成trie樹的形式,但是這有意義嗎?如果是一個完整的trie路由表,最長32步(考慮壓縮和回溯)就能找到結果,如果採用hash+trie的方式,每一步的最壞結果都是32步,一共進行32步...這樣做沒有意義。
       海量路由表項時,hash小空間是嚴格有範圍的,可以認為它是固定的,平均情況很容易通過地址空間和hash空間求得,最壞情況則是完全遍歷。平均情況如果都不能接受,難道值得為最好情況去冒險嗎?因此,千萬別用hash表儲存海量路由表項。
       但是,還沒完

7.區域性性利用以及DoS

32位系統,CPU Cache相比記憶體而言非常小,怎麼可以帶來如此大的優化?所有對映到同一個cache line的地址都是衝突的啊...這是因為CPU Cache利用了程式的時間/空間區域性性,而對於路由而言,則沒有空間區域性性。時間區域性性可以用於路由cache,然而用於路由表本身則有難度。路由表和CPU Cache的區別在於它是完全的,不存在被替換和老化的問題,因此可以把好的hash函式用於單獨的路由cache,而路由表僅僅用於路由cache不命中的情況下去匹配。
       理想情況分析完了,剩下的只是悲哀了。
       網路訪問的時間區域性性真的可以利用嗎?雖然一個5元組的資料流一般會隨著時間持續經過路由器,但是如果hash衝突的另一個數據流也經過的話,就會造成cache抖動,在CPU Cache看來,這個問題可以通過控制task切換或者增加cache line唯一鍵值來解決,可是對於網路訪問,你沒法阻止任何一個數據包的到來,只要到來就要查詢路由表,就有可能導致cache抖動。更嚴重的,路由cache很容易受到精心構造的資料包的攻擊造成不可用,頻繁的替換或者無限的加長連結串列,平添了查詢開銷。
       因此設計一個完全的轉發表而不是利用路由cache更加能提升轉發效率。這又一次為我的DxR Pro結構作了一個廣告。

相關推薦

海量路由可以使用HASH儲存-HASH查詢TRIE查詢

千萬別!很多人這樣說,也包括我。Linux核心早就把HASH路由表去掉了,現在就只剩下TRIE了,不過我還是希望就這兩種資料結構展開一些形而上的討論。1.hash和trie/radixhash和tire其實是可以統一在一起的。具有相同hash值的多個項具有一個共同的特徵,這個

七大查詢演算法之查詢---二叉查詢演算法

二叉樹查詢演算法        二叉查詢樹是先對待查詢的資料進行生成樹,確保樹的左分支的值小於右分支的值,然後在就行和每個節點的父節點比較大小,查詢最適合的範圍。 這個演算法的查詢效率很高,但是如果使用這種查詢方法要首先建立樹。 原理:         二叉查詢樹(Bi

用SQL語言進行復雜查詢:對各中的資料進行不同條件的連線查詢巢狀查詢: 1)查詢每個學生及其選課情況; 2)查詢每門課的間接先修課

對各表中的資料進行不同條件的連線查詢和巢狀查詢: 1)查詢每個學生及其選課情況; 2)查詢每門課的間接先修課 3)將STUDENT,SC進行右連線 4)查詢有不及格的學生姓名和所在系 5)查詢所有成績為優秀(大於90分)的學生姓名 6)查詢既選修了2號課程又選修了3號課程的

hash海量查詢字串(java版)

 雜湊表(散列表)是一種非常高效的查詢資料結構,在原理上也與其他的查詢不盡相同,它迴避了關鍵字之間反覆比較的繁瑣,而是直接一步到位查詢結果。當然,這也帶來了記錄之間沒有任何關聯的弊端。應該說,散列表對於那些查詢效能要求高,記錄之間關係無要求的資料有非常好的適用性。注意對雜

C語言 開放定址法HASH儲存簡單實現

#include<stdio.h> #include<stdlib.h> #include<malloc.h> #include<string.h> #include<time.h> #include<cty

nyist oj 138 找球號(二)(hash +位運算)

pan char s geo 運算 arch font msu 哈希 next 找球號(二) 時間限制:1000 ms | 內存限制:65535 KB 難度:5 描寫敘述 在某一國度裏流行著一種遊戲。遊戲規則為:現有一堆球中。每一個球上都有一個整數編號i

Java鏈接HBASE數據庫,創建一個,刪除一張,修改,輸出插入,修改,數據刪除,數據獲取,顯示信息,過濾查詢,分頁查詢,地理hash

can charat nfa true 目錄結構 dfa byte sin extra 準備工作 1、創建Java的Maven項目 創建好的目錄結構如下: 另外註意junit的版本,最好不要太高,最開始筆者使用的junit4.12的,發現運行的時候會報錯。最後把Junit

數據結構與算法-hash

數據結構與算法 技術分享 進行 return 等價 class 取值 href .cn 前言 哈希表是一種存放鍵-值對的數據結構,其中值用來存放我們真正需要的數據,鍵的主要目的就是為了找到值。哈希表理想情況下,只需要一次hash計算即可找到值數據,但通常情況下我們不需要耗費

Hash的平均查找長度ASL計算方法

hash表 ron red 二次 考研 table style 時間 長度 Hash表的“查找成功的ASL”和“查找不成功的ASL” ASL指的是 平均查找時間 關鍵字序列:(7、8、30、11、18、9、14) 散列函

計蒜客 成績統計 (Hash)

就是 space tdi string name esp turn std scan 鏈接 : Here! 思路 : 如果用 $STL$ 的 $map$ 或者是使用 $unordered\underline{}map$ 的話是會 $T$ 的, 所以得手寫一個 $has

memcached中hash相關操作

top this eof get 完整 啟動 哈希 作用 需要   以下轉自http://blog.csdn.net/luotuo44/article/details/42773231 memcached源碼中assoc.c文件裏面的代碼是構造一個哈希表。m

Hash

動態 常數 填充 include 方法 想要 hash toolbar 開放定址法 Hash表也稱散列表,也有直接譯作哈希表,Hash表是一種特殊的數據結構,它同數組、鏈表以及二叉排序樹等相比較有很明顯的區別,它能夠快速定位到想要查找的記錄,而不是與表中存在的記錄的關鍵字進

散列表(hash)

關系 技術 優點 圖片 數據 span height 中間 表頭 1. hash表: 又稱散列表,以key-value的形式存儲數據,能夠由key快速定位到其指定的value,而不經過查找。它采用了函數式的映射思想,將記錄的存儲位置與關鍵詞相關聯,從而快速定

Hash分析以及Java實現

   這篇部落格主要探討Hash表中的一些原理/概念,及根據這些原理/概念,自己設計一個用來存放/查詢資料的Hash表,並且與JDK中的HashMap類進行比較。 我們分一下七個步驟來進行。  一。    Hash表概念 二 . &n

luogu4407 [JSOI2009]電子字典 字串hash + hash

暴力列舉,然後\(hash\)表判斷 複雜度\(O(26 * 20 * n)\) 具體而言 對於操作1:暴力列舉刪除 對於操作2:暴力新增,注意新增不要重複 對於操作3:暴力替換,同樣的注意不要重複 #include <cstdio> #include <cstr

php 實現hash

php的陣列實際上就是hash_table,無論是 數字索引陣列array(1, 2, 3) 還是關聯陣列array(1 => 2, 2=> 4)等等。  PHP中雜湊表結構 假定向PHP陣列中插入三個元素分別為Bucket1,Bucket2,Bucket3,其中Buc

linux核心 hash的基本使用

栗子如下: #include <stdio.h> #include <stdlib.h> #include <string.h> #include "hlist.h" typedef struct list_test{ struct hlist_no

java hash

當使用一個雜湊表,要指定用作鍵的物件,以及要連結到該鍵的值。 然後,該鍵經過雜湊處理,所得到的雜湊碼被用作儲存在該表中值的索引。   Hashtable定義了四個構造方法。第一個是預設構造方法: Hashtable() 第二個建構函式建立指定大小的雜湊表: Hashtable(int s

演算法基礎之--hash新增,刪除,擴容

package wck.sort; import java.util.TreeMap; /** * 二次封裝hashtable。基於陣列和TreeMap * @param <K> * @param <V> * */

hash--c語言 字串鍵值配對——(key, value)

c語言鍵值配對——(key, value) 看一個C++專案時,其中解析配置文的部分引發了我的思考。 配置檔案問普通字元檔案,內容都是類似 如下: ipaddr=127.0.0.1 port=888 logfile=log C++對此配置檔案解析字