1. 程式人生 > >HBase表設計介紹

HBase表設計介紹

概述

在不久的過去,大資料的應用越來越多。為了支援這些應用以及擴充套件老的應用,很多新的資料管理系統被開發出來,被稱作大資料革命。這些系統中很多都是開源和社群驅動的。Apache Hbase就是這樣的一個系統,是一個開源的分散式的資料庫,和Google Bigtable類似。並且發展迅速,為那些需要快速隨機訪問的大資料應用提供了好的選擇。建立在Apache Hadoop之上,並且和Hadoop緊密的整合。

HBase和傳統的資料庫有很大的不同之處,比如MySQL,PostGreSQL,Oracle等。在架構和提供的特性方面都有不同之處,HBase去掉了一些伸縮和靈活性的特性,這也就使得hbase擁有一個非常不同的資料模型。設計hbase的表和傳統關係資料庫非常不同。我會通過解釋hbase資料模型以及通過一些例項來介紹hbase表的基本設計。

Hbase資料模型

hbase資料模型和關係型資料庫是非常不同的。就像Bigtable描述的那樣,這是一個稀疏的,分散式的,持久化的,多維的,排序的對映,索引通過行鍵,列鍵以及時間戳來實現。你可能聽別人把它當做一個key-value儲存結構的,或者說是面向列族的資料庫。或者說是多版本對映的資料庫。所有的這些描述都是正確的。這個章節,我們介紹一下這些概念。

hbase資料模型最讓人可以接收的描述就是使用表,行和列。這和關係型資料庫很像。但是也就是名稱相似而已,行列的概念還是略有不同的,下面我們介紹一下這些概念。

表(Table)

hbase在表中組織資料。表名是字串和字元的組合,可以在檔案系統路徑中使用。

行(Row)

在表中資料依賴於行來儲存,行通過行鍵來區分。行鍵沒有資料型別,通常是一個位元組陣列。

列族(Column Family)

行中的資料通過列族來組織。列族也暗示了資料的物理排列。所以列族必須預先定義,並且不容易被修改。每行都擁有相同的列族,可能有些行的資料為空。列族是字串和字元的組合,可以在檔案系統路徑
中使用。

列標識(Column Qualifier)

資料在列族中的位置是通過列標識來指定的。列標識不需要預先指定,每行的列標識也不需要相同。就像行鍵一樣,列標識沒有資料型別,通常也是位元組陣列。

單元(Cell)

單元是行鍵、列族、列標識的組合。這些資料儲存在單元中,被稱作單元資料。資料也不需要資料型別,通常也是位元組陣列。

時間戳(Timestamp)

單元資料是有版本的。版本的區分就是他們的版本號,版本號預設就是時間戳。當寫入資料時,如果沒有指定時間,那麼預設的時間就是系統的當前時間。讀取資料的時候,如果沒有指定時間,那麼返回的就是最新的資料。保留版本的數量根據每個列族的配置。預設的版本數量是3。

hbase中的一個表就像下面這張圖:
這裡寫圖片描述

在這個圖中,表包含兩個列族,personal和office。每個列族都有兩列。每個方格就是一個單元,行鍵根據字母順序進行排序。
這些概念也通過API方式暴露給客戶端。hbase的API資料管理包含三個主要方法:get,put和scan。get和put方法需要制定行鍵,scan操作是瀏覽一定範圍的行。範圍可以通過開始和結束的行鍵來指定。如果不指定那麼就是瀏覽整個表資料。有時候,使用多維對映來理解資料模型可能更簡單。多維對映就像下圖所示:
這裡寫圖片描述

行鍵對映一個列族的列表,列族對映一個列標識的列表,列標識對映一個時間戳的列表,每個時間戳對映一個值,也就是單元值。如果你使用行鍵來檢索對映的資料,那麼你會得到所有的列。如果你檢索特定列族的資料,你會得到此列族下所有的列標識。如果你檢索列標識所對映的資料,你會得到所有的時間戳以及對應的資料。hbase優化了返回資料,預設僅僅返回最新版本的資料。當然你也可以得多一個多版本返回。行鍵和關係資料庫中的主鍵有相同的作用,你不能改變列的行鍵,換句話說就是,如果表中已經插入資料,那麼personal列族中的列名不能改變它所屬的行鍵。

就像之前提到的,你可以使用不同的方式來理解資料模型。當然你也可以使用鍵值的方式來理解,鍵就是行鍵,值就是列中的值,但是給定一個行鍵僅僅能確定一行的資料。你可以把行鍵,列族,列標識,時間戳都看做鍵。而值就是單元中的資料。當你深入入到儲存層的時候,你會看到如果你想要讀取一個特定單元資料的時候,你就會先得到一個數據庫塊,這個塊也會包含其他單元的資料。下面是鍵值結構圖:

這裡寫圖片描述

HBase表設計基礎

之前的章節已經介紹,hbase的資料模型和關係型資料庫是十分不同的。那麼hbase的表設計就和關係資料庫表設計有很大不同之處。設計hbase表需要回答下面的問題:

  • 1.行鍵的結構是什麼的並且要包含什麼內容?
  • 2.表有多少個列族?
  • 3.列族中都要放什麼資料?
  • 4.每個列族中有多少個列?
  • 5.列名是什麼?儘管列名在建立表時不需要指定,你讀寫資料是需要用到它們。
  • 6.單元資料需要包含哪些資訊?
  • 7.每個單元資料需要儲存的版本數量是多少?

定義hbase表最重要的事情就是行鍵的結構。為了更有效的定義,首先定義訪問模式是很重要的。為了定義表的結構,一些hbase特定的屬性是需要考慮在內的,如下所示:

  • 1.索引僅僅依賴於Key
  • 2.表資料根據行鍵排序,表中的每個區域都代表了一部分行鍵的空間,這個區域通過開始和結束行鍵來指定
  • 3.hbase表中的資料都是位元組陣列,沒有型別之分。
  • 4.原子性僅保證在行級。跨行操作不保證原子性,也就是說不存在多行事務。
  • 5.列族必須在表建立的時候就定義。
  • 6.列標識是動態的,可以在寫入資料是定義。

一個好的方法去學習這些感念就是通過一個例子。下面我們使用hbase表來設計推特的使用者關係(使用者關注另一個使用者)。關注和被關注的關係實際上就是圖,使用指定的圖資料庫可能工作的更加高效。然而,這裡只是一個使用用例來理解hbase的一些概念。首先我們來設計表的訪問模式。訪問模式定義如下:

讀模式:

  • 1.使用者關注了誰?
  • 2.是否使用者A關注了使用者B?
  • 3.都有誰關注了使用者A?

寫模式:

  • 1.使用者關注了一個新的使用者。
  • 2.使用者取消關注。

下面我們使用遞進式的方法考慮幾種表設計,並看她們的優缺點。第一種設計如下:

這裡寫圖片描述

在一行中儲存被關注的使用者的列表(也就是此使用者都關注了誰),行鍵是使用者ID。每一列包含關注的使用者ID。表中的資料如下:

這裡寫圖片描述

這個表設計滿足讀模式的1和2。但是要想獲得都有誰關注了此使用者是非常耗時的,需要遍歷整個表。新增一個關注的使用者也是很棘手的,因為沒有一個計數器來表示新增的使用者是第幾個,除非你遍歷之前新增的所有的使用者,這也有些不合理。一種可用的解決方法就是使用一個額外的列來儲存一個計數器。如下圖所示:

這裡寫圖片描述

當新增關注使用者的時候,要實時的更新count的值。
這個設計比最早的設計要好一點,但不能解決所有的問題。取消關注也是棘手的,因為你要遍歷整行的資料來找到具體要刪除的使用者。而且刪除之後計數器將會產生漏洞。

我們之前提到過,列標識是動態的,以位元組陣列的方式儲存。這就可以讓你使用任意的資料來代表列標識。考慮一下下面的設計,在這個設計中,數量不是必須的。所以新增使用者變得很簡單,取消關注也是很簡單的。單元資料可以儲存任意的值對結果都沒有影響。

這裡寫圖片描述

這個設計解決了定義的大部分的訪問模式。僅僅只有讀模式3沒有滿足:誰關注了此使用者。當前設計中,行鍵僅僅是使用者ID,你需要瀏覽整個表資料來得到結果。這就讓我們想到能不能在行鍵指出被關注的使用者ID。有兩種方法來解決這個問題。第一種就是維護另外一張表包含反向列表(也就是此使用者關注的使用者列表)。第二種方法就是在一張表中使用不同的行鍵來儲存關注和被關注的資料。上面的兩種方法,都會實現資訊的分隔,以至於你可以快速的訪問。

對於當前表結構還可以進一步的優化,考慮下面的設計:

這裡寫圖片描述

有兩件事需要指出:行鍵現在包括關注著和被關注者;列族的名稱已經縮短為f。短的列族名稱沒有任何影響,它僅僅是為了提高IO操作。這裡獲得一個被關注的列表變成了一個部分瀏覽,而不是之前的全表瀏覽。取消關注和使用者A是否關注使用者B變成了簡單的刪除和get操作,而不是之前的遍歷整行。這在關注者和別關注者列表很大的時候將變得很有用。表中資料如下所示:

這裡寫圖片描述

需要注意的是行鍵的長度是變化的。變化的長度使得監控效能變得困難,因為來自每個請求的長度不一致。一個解決方法就是行鍵使用雜湊值,為了得到長度一致的行鍵,你可以先雜湊使用者的id然後連線他們,而不是簡單的連線在一起。在查詢的時候,因為你知道你要查詢的使用者ID,所以你可以重新計算雜湊後,在進行查詢。進行雜湊後的表像下面這樣:

這裡寫圖片描述

這個表設計可以有效的滿足所有的訪問模式。

總結

這篇文章包含了hbase的基礎架構設計,開始介紹了資料模型,並介紹了在設計表結構是需要注意的事項。當然關於表設計還有很多需要去探索的地方。這篇文章的關鍵點如下:

  • 1.行鍵在表設計中非常重要,決定著應用中的互動以及提取資料的效能。
  • 2.hbase表示非常靈活的,你可以使用位元組陣列儲存任何資料。
  • 3.存錯任何資料到列族中,都可以使用相同的訪問模式來訪問資料。
  • 4.索引僅僅是行鍵,好好利用,將成為你的優勢。
  • 5.深度高的表結構,可以使得你快速且簡單的訪問資料,但是卻丟掉了原子性。寬度廣的表結構,可以保證行級別的原子操作,但每行會有很多的列。
  • 6.你需要好好的思考你的表設計,使得可以使用單條API就可以操作,而不是使用多條。hbase不支援跨行的事務,也儘量避免在客戶端程式碼中使用這樣的邏輯。
  • 7.行鍵的雜湊可以使得行鍵有固定的長度和更好的分佈。但是卻丟棄了使用字串時的預設排序功能。
  • 8.列標識可以用來儲存資料,就像單元資料一樣。
  • 9.列標識的長度影響資料儲存的足跡。也影響硬碟和網路IO的花銷,所以應該儘量簡潔。
  • 10.列族名字的長度影響到傳送到客戶端的資料長度。所以儘量簡潔。