存儲與檢索 -- 為數(shù)據(jù)庫提供動力的數(shù)據(jù)結(jié)構(gòu)(哈希索引)

讓我們從鍵值數(shù)據(jù)的索引開始璧瞬。這不是你可以索引的唯一類型的數(shù)據(jù)金蜀,但它非常常見毙死,而且它是構(gòu)建更復雜索引的一個有用的模塊柜去。

鍵值存儲與大多數(shù)編程語言中可以找到的dictionary類型非常相似,通常是作為散列表實現(xiàn)的郊酒。在許多算法教科書中都描述了哈希映射遇绞,因此我們不會詳細討論它們是如何工作的。既然我們已經(jīng)有了內(nèi)存數(shù)據(jù)結(jié)構(gòu)的哈希映射燎窘,為什么不使用它們來索引磁盤上的數(shù)據(jù)呢?

假設(shè)我們的數(shù)據(jù)存儲只包括追加記錄到一個文件摹闽,就像前面的例子一樣。然后褐健,最簡單的索引策略是:保存一個內(nèi)存中的散列映射付鹿,其中每個鍵都映射到數(shù)據(jù)文件中的一個字節(jié)偏移位置,這個位置的值如圖3-1所示可以被找到蚜迅。當你在文件中添加新的鍵值對時舵匾,您還將更新散列映射以反映您剛剛編寫的數(shù)據(jù)的偏移量(這既用于插入新鍵,也用于更新現(xiàn)有的鍵)谁不。當您想查找一個值時坐梯,使用散列映射來查找數(shù)據(jù)文件中的偏移量,查找該位置刹帕,并讀取該值吵血。

圖3-1 以類csv的格式存儲鍵值對谎替,并使用內(nèi)存散列映射進行索引。

這聽起來可能過于簡單蹋辅,但卻是可行的方法钱贯。實際上,這就是Bitcask (Riak的默認存儲引擎)所做的事情晕翠。Bitcask提供高性能的讀寫喷舀,根據(jù)要求,所有的鍵都可以放在可用的RAM中淋肾,因為哈希映射被完全保存在內(nèi)存中硫麻。這些值可以使用比可用內(nèi)存更多的空間,因為它們可以從磁盤上加載樊卓。如果數(shù)據(jù)文件的那一部分已經(jīng)在文件系統(tǒng)緩存中拿愧,那么read不需要任何磁盤I/O。

像Bitcask這樣的存儲引擎非常適合于每個密鑰的值經(jīng)常更新的情況碌尔。例如浇辜,密鑰可能是一只貓的視頻URL,其值可能是它被播放的次數(shù)(每次有人點擊播放按鈕時遞增)唾戚。在這種模式中柳洋,有很多寫操作,但是沒有太多不同的鍵——每個鍵有大量的寫叹坦,但是將所有的鍵保存在內(nèi)存中是可行的熊镣。

到目前為止,我們只討論了追加記錄到文件中募书,那么如何避免最終耗盡磁盤空間呢?一個好的解決方案是绪囱,當它達到一定的大小時,關(guān)閉一個segment文件莹捡,然后將其寫入一個新的segment文件鬼吵,從而將日志分割成一定大小的segment。然后我們可以對這些片段執(zhí)行壓縮篮赢,如圖3-2所示齿椅。壓縮意味著在日志中丟棄重復的鍵启泣,并且只保留每個鍵的最新更新媒咳。

圖3-2 鍵值更新日志的壓縮(計算貓視頻播放的次數(shù)),只保留每個鍵的最新值

此外种远,由于壓縮常常使segment更小(假設(shè)一個鍵在一個segment內(nèi)平均覆蓋了幾次)顽耳,我們還可以同時合并多個segment坠敷,同時執(zhí)行壓縮妙同,如圖3-3所示。segment在寫入后不會被修改膝迎,因此合并的segment被寫入到一個新文件中粥帚。歷史segment的合并和壓縮可以在后臺線程中完成,而在進行的過程中限次,我們?nèi)匀豢梢允褂门f的segment文件繼續(xù)服務讀和寫請求芒涡。在合并過程完成之后,我們將讀取請求改為使用新的合并segment而不是舊的segment卖漫,然后刪除舊的segment文件费尽。

圖3-3 執(zhí)行壓縮和segment合并

每個segment現(xiàn)在都有自己的內(nèi)存哈希表,映射鍵來進行文件偏移羊始。為了找到密鑰的值旱幼,我們首先檢查最近segment的散列映射;如果密鑰不存在突委,我們將檢查第二個最近的部分柏卤。。匀油。合并進程保持了segment的數(shù)量很少缘缚,所以查找不需要檢查很多散列映射。

這個簡單的想法在實踐中有很多細節(jié)敌蚜。簡而言之桥滨,真正實施的一些重要問題是:

文件格式
CSV不是日志的最佳格式。使用二進制格式(以字節(jié)為單位編碼字符串的長度)和原始字符串(不需要轉(zhuǎn)義)會更快更簡單钝侠。

刪除記錄
如果您想刪除一個鍵及其相關(guān)值该园,則必須將一個特殊的刪除記錄附加到數(shù)據(jù)文件(有時稱為tombstone)。當segment被合并時帅韧,tombstone告訴合并進程放棄刪除鍵的任何先前值里初。

崩潰恢復
如果數(shù)據(jù)庫重新啟動,內(nèi)存中的散列映射就會丟失忽舟。原則上双妨,你可以通過從頭到尾讀取整個segment文件來恢復每個segment的哈希映射,并注意在進行過程中每個鍵的最新值的偏移量叮阅。但是刁品,如果segment文件很大,這可能需要很長時間浩姥,這會使服務重啟很痛苦挑随。Bitcask通過在磁盤上存儲每個segment的散列映射的快照來加速恢復,這可以更快地加載到內(nèi)存中勒叠。

部分文字記錄
數(shù)據(jù)庫可能在任何時候崩潰兜挨,包括將記錄追加到日志的過程中膏孟。Bitcask文件包括校驗和,允許檢測和忽略該日志中損壞的部分拌汇。

并發(fā)控制
由于寫入是嚴格按順序添加到日志中的柒桑,所以一個常見的實現(xiàn)選擇是只有一個寫線程。數(shù)據(jù)文件segment是只允許追加噪舀,而且是不可變的魁淳,以便于可以通過多個線程并發(fā)地讀取它們。

一個只允許追加的文件第一眼看上去很浪費:為什么不更新文件与倡,用新值重寫舊值界逛?但是只允許追加的設(shè)計是好的,有幾個原因:

  • 追加和segment合并是順序?qū)懖僮髡糇撸ǔ1入S機寫快得多仇奶,特別是在magnetic spinning-disk硬盤上。在某種程度上比驻,順序?qū)懺诨陂W存的固態(tài)硬盤(ssd)上也更可取该溯。我們將在后面的“比較BTrees和LSM-Trees”中進一步討論這個問題。
  • 如果segment文件是只允許追加的或不可變的别惦,那么并發(fā)和崩潰恢復要簡單得多狈茉。例如,您不必擔心發(fā)生崩潰時當一個值被覆蓋掸掸,會留下一個包含舊值的一部分氯庆,同時又將新值的一部分拼接在一起的文件。
  • 合并舊的segment可以避免數(shù)據(jù)文件隨著時間的推移而變得支離破碎的問題扰付。

然而堤撵,哈希表索引也有限制:

  • 哈希表必須適合內(nèi)存,所以如果你有非常多的鍵羽莺,你就不走運了实昨。原則上,您可以在磁盤上維護一個散列映射盐固,但不幸的是荒给,很難使磁盤上的哈希映射執(zhí)行良好。它需要大量的隨機訪問I/O刁卜,而且當它很大時志电,再增長的代價就比較昂貴了,并且散列沖突也需要復雜的邏輯蛔趴。
  • 范圍查詢無效挑辆。例如,您不能輕松地掃描kitty00000和kitty99999之間的所有key,您必須在散列映射中單獨查找每個鍵之拨。

在下一節(jié)中茉继,我們將研究一個沒有這些限制的索引結(jié)構(gòu)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蚀乔,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子菲茬,更是在濱河造成了極大的恐慌吉挣,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婉弹,死亡現(xiàn)場離奇詭異睬魂,居然都是意外死亡,警方通過查閱死者的電腦和手機镀赌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門氯哮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人商佛,你說我怎么就攤上這事喉钢。” “怎么了良姆?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵肠虽,是天一觀的道長。 經(jīng)常有香客問我玛追,道長税课,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任痊剖,我火速辦了婚禮韩玩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘陆馁。我一直安慰自己找颓,他們只是感情好,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布氮惯。 她就那樣靜靜地躺著叮雳,像睡著了一般。 火紅的嫁衣襯著肌膚如雪妇汗。 梳的紋絲不亂的頭發(fā)上帘不,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音杨箭,去河邊找鬼寞焙。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的捣郊。 我是一名探鬼主播辽狈,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼呛牲!你這毒婦竟也來了刮萌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娘扩,失蹤者是張志新(化名)和其女友劉穎着茸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琐旁,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡涮阔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了灰殴。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敬特。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖牺陶,靈堂內(nèi)的尸體忽然破棺而出伟阔,到底是詐尸還是另有隱情,我是刑警寧澤义图,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布减俏,位于F島的核電站,受9級特大地震影響碱工,放射性物質(zhì)發(fā)生泄漏娃承。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一怕篷、第九天 我趴在偏房一處隱蔽的房頂上張望历筝。 院中可真熱鬧,春花似錦廊谓、人聲如沸梳猪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽春弥。三九已至,卻和暖如春叠荠,著一層夾襖步出監(jiān)牢的瞬間匿沛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工榛鼎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留逃呼,地道東北人鳖孤。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像抡笼,于是被迫代替她去往敵國和親苏揣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容