天天看點

如何優雅的了解HBase和BigTable

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

學習 HBase 最難的地方在于要讓你的腦子真正了解它是什麼。

HBase:Google BigTable 的開源實作

我們經常會把關系型資料庫(RDBMS,比如 MySQL)和 HBase 搞混,因為在這兩個系統中都包含 table 和 base(HBase,Database)。

這篇文章的目标是從概念上來說清楚 HBase 這個分布式的資料存儲系統。讀完後,你應該可以很清楚的知道什麼情況下 HBase 更好,什麼情況下傳統的關系型資料庫更好。

關于一些術語

幸運的是,Google 的 BigTable論文清楚的解釋了 BigTable 到底是什麼。下面是論文中資料模型章節的第一句話:

BigTable 是一個稀疏的、分布式的、可持久化的多元有序 map。

在這個節骨眼上,我想給讀者一個機會,讓他們在讀到最後一行字時,能夠收集到他們腦殼裡的活動資訊(這可能是個笑話,但我沒懂^v^)。

論文中,繼續解釋如下:

map 通過 rowKey,columnKey 和時間戳進行索引,map 中的每個值都是一個連續的位元組數組。

注:rowKey 是記錄的主鍵,唯一辨別一行記錄

在 Hadoop 的官方文檔中,也對 HBase 的架構做了說明:

HBase 使用了與 BigTable 非常類似的資料模型。使用者存儲資料行到特定的表中。一個資料行有一個可排序的 rowKey 和數量不定的列。這個表是稀疏的,隻要使用者願意,這個表不同行可以有完全不同的列。

這些話看起來相當費解,讓人摸不着頭腦,但如果你把這些話拆成一個個詞,意思就慢慢變的清晰了。我将按照以下的順序來讨論這些詞:map,持久化,分布式,有序的,多元的,稀疏。

我發現循序漸進地建立一個思維架構要比一次性勾畫一個完整的系統更加容易。

map

從根本來上來,HBase/BigTable 是一個 map。map 在不同的程式設計語言中有不同的叫法,比如 PHP 中的 array,Python 的 dictionary,Ruby 中的 Hash,或者 JavaScript 中的 Object。

維基百科上對于 map 的定義是:map 是一個抽象的資料類型,包含了一組 key 和一組 value,每個 key 關聯一個 value。

如果用 JavaScript 的對象來表示 map,這裡有一個簡單的例子,其中所有的 value 都是字元串:

{
  "zzzzz" : "woot",
  "xyz" : "hello",
  "aaaab" : "world",
  "1" : "x",
  "aaaaa" : "y"
}           

持久化的

持久化的意思僅僅是指你放進這個特殊 map 的資料會在你的程式執行完成之後被儲存下來。它和其他的持久化存儲系統中持久化的概念沒有任何差別,比如存一個檔案到一個檔案系統。我們繼續...

分布式的

HBase 和 BigTable 都建立在分布式檔案系統上,是以底層檔案可以被分散存儲到不同的機器上。

HBase 可以存儲到 HDFS(Hadoop's Distributed File System)上,也可以存儲到 亞馬遜的 S3(Simple Storage Service)上,而 BigTable 使用的是 GFS(Google File System)。

同一份資料會被複制存儲到多個節點上,類似于 RAID(獨立備援磁盤陣列,利用備援存儲的資料使損壞資料得以恢複,進而保護資料不丢失)系統中資料在磁盤上的複制存儲到多塊磁盤的方式。

在這篇文章中,我們不關心具體使用哪種分布式檔案系統。重要的是,要了解這個檔案系統是分布式的,即使叢集中某個節點出現故障,也可以保證資料的完整性和安全性。

有序的

和其他大多數 map 的實作不同,HBase 和 BigTable 的鍵值對的順序嚴格按照字母順序來排列。是以 rowKey 為 "aaaaa" 的下一條記錄的 rowKey 就是 "aaaab",并且會離 “zzzz” 非常遠。

繼續看上面的那個 JSON 例子,排行序之後是下面這樣的:

{
  "1" : "x",
  "aaaaa" : "y",
  "aaaab" : "world",
  "xyz" : "hello",
  "zzzzz" : "woot"
}
           

因為這個系統是分布式的,而且會越來越大,是以排序這個特性非常重要。這樣就會把 rowKey 相近的記錄放在一起,在某些情況下,如果你必須要掃描表(通常不推薦),那就能保證你需要擷取的記錄都在一塊。

那麼如何選擇 rowKey 就非常重要。比如說,一個表的 rowKey 就是域名。一個比較好的方式就是将域名進行反轉來作為 rowKey(使用 “com.jimbojw.www”,而不要使用 “www.jimbojw.com”),這樣,同一個域名下的記錄就可以存儲在相鄰的位置。

繼續上面的域名例子,rowKey 為 “mail.jimbojw.com” 行應該與 “www.jimbojw.com” 行更近,而不是 “mail.xyz.com”,如果不把域名反轉存儲,就會發生這種情況。

需要注意的是,在 HBase / BigTable 中,有序并不意味着值是有序的。除了 rowKey 以外,沒有任内容會被排序,在這點上和普通 map 的實作一緻。

多元的

到目前為止,我們還沒有提過任何關于列的概念,而是将表在概念上當做正常的 map。我是故意這麼做的。列和表、base 等詞一樣,都帶有傳統關系型資料庫多年的情感包袱。

然而,我發現把 HBase 了解為一個多元的 map 會容易很多,map 的 map。給上面的 JSON 再加上一列:

{
  "1" : {
    "A" : "x",
    "B" : "z"
  },
  "aaaaa" : {
    "A" : "y",
    "B" : "w"
  },
  "aaaab" : {
    "A" : "world",
    "B" : "ocean"
  },
  "xyz" : {
    "A" : "hello",
    "B" : "there"
  },
  "zzzzz" : {
    "A" : "woot",
    "B" : "1337"
  }
}
           

在上面的例子中你可以看到每個 key 都指向了另一個 map,其中包含着 A 和 B 兩個 key。在這裡,我們将最上面那層鍵值對稱為行。并且在 HBase / BigTable 的術語表中,A 和 B 的映射稱之為列族。

一個表的列族在表建立的時候就會被建立好,而且後續修改很困難,添加一個新列族的開銷同樣也很大,是以在建立表的時候應當将後續會用到的所有列族建立好。

好在一個列族可以有任意數量的列。稱之為為列限定符(qualifier)或者标簽(label)。

下面是我們上面 JSON 例子的子集,這次加入了 qualifier 的次元:

{
  // ...
  "aaaaa" : {
    "A" : {
      "foo" : "y",
      "bar" : "d"
    },
    "B" : {
      "" : "w"
    }
  },
  "aaaab" : {
    "A" : {
      "foo" : "world",
      "bar" : "domination"
    },
    "B" : {
      "" : "ocean"
    }
  },
  // ...
}
           

注意在上面的兩行資料中,A 列族有兩列:foo 和 bar,B 列族隻有一列,而且 qualifier 是一個空字元串。

當通路 HBase / BigTable 中的資料時,你需要提供完整的列名::。舉個例子,上面總共有三列,分别是:A:foo,A:bar 和 B:。

列族雖然基本固定不變,但是列不是,來看下面的例子:

{
  // ...
  "zzzzz" : {
    "A" : {
      "catch_phrase" : "woot",
    }
  }
}
           

在這個例子中,zzzzz 行有一個列 A:catch_phrase。因為每一行可以有任意數量的列,是以沒有内置方法可以從所有行中的所有列中查詢出一個清單。為了擷取到那些資訊,你需要做全表掃描。但是你可以查詢所有的列族,因為它們是不變的(基本不變)。

HBase / BigTable 中最後的一個次元是時間。所有資料預設通過時間戳(1970年以來的秒數)來表示版本,或者你也可以指定一個其他的整數。用戶端在插入資料的時候可以指定這個時間戳。

在最新的例子中,我們使用任意的整數來作為版本辨別:

{
  // ...
  "aaaaa" : {
    "A" : {
      "foo" : {
        15 : "y",
        4 : "m"
      },
      "bar" : {
        15 : "d",
      }
    },
    "B" : {
      "" : {
        6 : "w"
        3 : "o"
        1 : "w"
      }
    }
  },
  // ...
}
           

每個列族可以自己指定一個 cell 中的資料可以保留多少個版本(cell 由 rowKey 和列進行辨別)。在大多數情況下,應用會直接通路一個 cell 中的資料,而不會指定一個時間戳(版本),HBase / BigTable 會直接傳回最近版本(時間戳最大的那個)的資料,因為它是按照時間倒序來存儲資料的。

如果應用在請求資料的時候指定了一個時間戳,那麼 HBase 就會傳回時間戳小于或者等于指定時間戳的一個 cell 中的資料。

如果查詢上面例子中的 HBase 表,查詢 aaaaa A:foo,就會傳回 y,如果帶時間戳查詢 aaaaa A:foo 10,就會傳回 m,如果查詢 aaaaa A:foo 2,就會傳回 null。

稀疏的

最後的一個關鍵詞是稀疏的。就如上面所說的,一個給定的行在每個列族中可以有任意數量的列,0 或者任意大。行之間可以存在間隙,這也是另一種稀疏。

如果你一直跟着本文在 map 的基礎上來了解 HBase / BigTable,而沒有與關系型資料庫(RDBMS)的概念混淆,這樣就很好了。

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/live

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-06-14

本文作者:Rayjun

本文來自:“

掘金

”,了解相關資訊可以關注“掘金”

繼續閱讀