Hbase一些問題點

序言

搭建了Hbase的服務後,可以對照傳統數據庫去專門的搜索他的功能.這是會發現一些問題.比如只支持一級索引,只支持行級事務.故此要了解針對這些問題的解決方案.這個別文章其實是整合了其它人的勞動成果.當然也歡迎大家聊一下,雖然現在認真聊個事情是非常難.哈哈~但是萬一有人有想法呢.請聯繫[email protected]

https://blog.csdn.net/Luomingkui1109/article/details/82762852

https://blog.csdn.net/lipeng_bigdata/article/details/50458771

 

關於Hbase查詢效率的解決方案

  目前 HBase 主要應用在結構化和半結構化的大數據存儲上,其在插入和讀取上都具有 極高的性能表現,這與它的數據組織方式有着密切的關係,在邏輯上,HBase 的表數據按 RowKey 進行字典排序, RowKey 實際上是數據表的一級索引(Primary Index),由於 HBase 本身沒有二級索引(Secondary Index)機制,基於索引檢索數據只能單純地依靠 RowKey,爲了能支持多條件查詢,開發者需要將所有可能作爲查詢條件的字段一一拼接到 RowKey 中,這是 HBase 開發中極爲常見的做法,但是無論怎樣設計,單一 RowKey 固有 的侷限性決定了它不可能有效地支持多條件查詢。

    通常來說,RowKey 只能針對條件中含有其首字段的查詢給予令人滿意的性能支持,在 查詢其他字段時,表現就差強人意了,在極端情況下某些字段的查詢性能可能會退化爲全表 掃描的水平,這是因爲字段在 RowKey 中的地位是不等價的,它們在 RowKey 中的排位決 定了它們被檢索時的性能表現,排序越靠前的字段在查詢中越具有優勢,特別是首位字段 具有特別的先發優勢,如果查詢中包含首位字段,檢索時就可以通過首位字段的值確定 RowKey 的前綴部分,從而大幅度地收窄檢索區間,如果不包含則只能在全體數據的 RowKey 上逐一查找,由此可以想見兩者在性能上的差距。

    受限於單一 RowKey 在複雜查詢上的侷限性,基於二級索引(Secondary Index)的解決 方案成爲最受關注的研究方向,並且開源社區已經在這方面已經取得了一定的成果,像 ITHBase、IHBase 以及華爲的 hindex 項目,這些產品和框架都按照自己的方式實現了二級 索引,各自具有不同的優勢,同時也都有一定侷限性。    

    關係型數據庫中索引數據與原始數據之間的數據一致性是通過關係型數據庫中的組件 負責實現的,對於數據庫數據的插入和刪除都會在索引中展現出來,對於原始數據和索引的 操作會是一個原子/事務操作,但是在 HBase 中沒有框架自身提供的機制,只能靠開發人員 自己去實現。

    由於在 HBase 中的二級索引是通過建表的方式實現的,當需要更新時,就是兩個表的 數據原子更新,也就是跨表的事務功能,而 Hbase 只提供行級事務,沒有跨表和跨行的事 務功能,這就需要開發者自己去實現,如果對數據一致性要求較高,那麼就可能需要自己 去實現一套分佈式的事務機制,之所以是分佈式的事務機制,是因爲原始數據可能由一些 HRegionserver 維護,而索引表由另外一些 HRegionserver 維護,這個事務機制就涉及到了多 個 HRegionserver,也就是分佈式的事務機制。因此,二級索引是 HBase 自身存在的一個短 板。

二級索引設置

    二級索引的本質就是建立各列值與行鍵之間的映射關係,以列的值爲鍵,以記錄的RowKey 爲值。

 

 

  如圖所示,當要對 F:C1 這列建立索引時,只需要建立 F:C1 各列值到其對應行鍵 的映射關係,如 C11->RK1 等,這樣就完成了對 F:C1 列值的二級索引的構建,當要查詢符 合 F:C1=C11 對應的 F:C2 的列值時(即根據 C1=C11 來查詢 C2 的值,圖 2-23 青色部分)。

    其查詢步驟如下:

    1. 根據 C1=C11 到索引數據中查找其對應的 RK,查詢得到其對應的 RK=RK1;

    2. 得到 RK1 後就自然能根據 RK1 來查詢 C2 的值了 這是構建二級索引大概思路,其 他組合查詢的聯合索引的建立也類似。


二級索引設計剖析

“二級多列索引”是針對目標記錄的某個或某些列建立的“鍵-值”數據,以列的值爲 鍵,以記錄的 RowKey 爲值,當以這些列爲條件進行查詢時,引擎可以通過檢索相應的“鍵 -值”數據快速找到目標記錄。由於 HBase 本身並沒有索引機制,爲了確保非侵入性,引擎 將索引視爲普通數據存放在數據表中,所以,如何解決索引與主數據的劃分存儲是引擎第 一個需要處理的問題。

    爲了能獲得最佳的性能表現,我們並沒有將主數據和索引分表儲存,而是將它們存放 在了同一張表裏,通過給索引和主數據的 RowKey 添加特別設計的 Hash 前綴,實現了在 Region 切分時,索引能夠跟隨其主數據劃歸到同一 Region 上,即任意 Region 上的主數據 其索引也必定駐留在同一 Region 上,這樣我們就能把從索引抓取目標主數據的性能損失降 低到最小。

    與此同時,特別設計的 Hash 前綴還在邏輯上把索引與主數據進行了自動的分離,當全 體數據按 RowKey 排序時,排在前面的都是索引,我們稱之爲索引區,排在後面的均爲主 數據,我們稱之爲主數據區。最後,通過給索引和主數據分配不同的 Column Family,又 在物理存儲上把它們隔離了起來。邏輯和物理上的雙重隔離避免了將兩類數據存放在同一 張表裏帶來的副作用,防止了它們之間的相互干擾,降低了數據維護的複雜性,可以說這是 在性能和可維護性上達到的最佳平衡。

 

 

 讓我們通過一個示例來詳細瞭解一下二級多列索引表的結構:

    假定有一張 Sample 表,使用四位數字構成 Hash 前綴,範圍從 0000 到 9999,規劃切分 100 個 Region,則 100 個 Region 的 RowKey 區間分別爲[0000,0099],[0100,0199],......, [9900,9999]。

    以第一個 Region 爲例,請看圖 2-23,所有數據按 RowKey 進行字典排序,自動分成了 索引區和主數據區兩段,主數據區的 Column Family 是 d,下轄 q1,q2,q3 等 Qualifier,爲了 簡單起見,我們假定 q1,q2,q3 的值都是由兩位數字組成的字符串,索引區的 Column Family 是 i,它不含任何 Qualifier,這是一個典型的“Dummy Column Family“,作爲區別於 d 的 另一個 Column Family,它的作用就是讓索引獨立於主數據單獨存儲

    接下來是最重要的部分,即索引和主數據的 RowKey,我們先看主數據的 RowKey,它 由四位 Hash 前綴和原始 ID 兩部分組成,其中 Hash 前綴是由引擎分配的一個範圍在 0000 到 9999 之間的隨機值,通過這個隨機的 Hash 前綴可以讓主數據均勻地散列到所有的 Region 上,我們看圖 1,因爲 Region 1 的 RowKey 區間是[0000,0099],所以沒有任何例外,凡是且 必須是前綴從 0000 到 0099 的主數據都被分配到了 Region 1 上。

 

接下來看索引的 RowKey,它的結構要相對複雜一些,格式爲:RegionStartKey-索引名 -索引鍵-索引值,與主數據不同,索引 RowKey 的前綴部分雖然也是由四位數字組成,但卻 不是隨機分配的,而是固定爲當前 Region 的 StartKey,這是非常重要而巧妙的設計,一方 面,這個值處在 Region 的 RowKey 區間之內,它確保了索引必定跟隨其主數據被劃分到同 一個 Region 裏;另一方面,這個值是 RowKey 區間內的最小值,這保證了在同一 Region 裏所有索引會集中排在主數據之前。接下來的部分是“索引名”,這是引擎給每類索引添 加的一個標識,用於區分不同類型的索引,圖 1 中展示了兩種索引:a 和 b,索引 a 是爲字 段 q1 和 q2 設計的兩列聯合索引,索引 b 是爲字段 q2 和 q3 設計的兩列聯合索引,依次類 推,我們可以根據需要設計任意多列的聯合索引。再接下來就是索引的鍵和值了,索引鍵 是由目標記錄各對應字段的值組成,而索引值就是這條記錄的 RowKey

    現在,假定需要查詢滿足條件 q1=01 and q2=02 的 Sample 記錄,分析查詢字段和索引 匹配情況可知應使用索引 a,也就是說我們首先確定了索引名,於是在 Region 1 上進行 scan 的區間將從主數據全集收窄至[0000-a, 0000-b),接着拼接查詢字段的值,我們得到了索引鍵: 0102,scan 區間又進一步收窄爲[0000-a-0102, 0000-a-0103),於是我們可以很快地找到 0000-a-0102-0000|63af51b2 這條索引,進而得到了索引值,也就是目標數據的 RowKey: 0000|63af51b2,通過在 Region 內執行 Get 操作,最終得到了目標數據。需要特別說明的是 這個 Get 操作是在本 Region 上執行的,這和通過 HTable 發出的 Get 有很大的不同,它專 門用於獲取 Region 的本地數據,其執行效率是非常高的,這也是爲什麼我們一定要將索引 和它的主數據放在同一張表的同一個 Region 上的原因。

 

 

關於Hbase的行級鎖

 

什麼是行鎖?

        我們知道,數據庫中存在事務的概念。事務是作爲單個邏輯工作單元執行的一系列操作,要麼完全地執行,要麼完全的不執行。而事務的四大特點即原子性、一致性、分離性和持久性。其中,原子性首當其衝,那麼在HBase內部實現其原子性的重要保證是什麼呢?答案就是行鎖。

        什麼是行鎖呢?顧名思義,它就是加在行上的一把鎖。在它未釋放該行前,最起碼其他訪問者是無法對該行做修改的,即要修改的話,必須得獲得該行的鎖才能擁有修改改行數據的權限,這就是行鎖的含義。

HBase行鎖實現原理

        HBase行鎖是利用Java併發包concurrent裏的CountDownLatch(1)來實現的。它的主要思想就是在服務器端每個訪問者單獨一個數據處理線程,每個處理線程針對特定行數據修改時必須獲得該行的行鎖,而其他客戶端線程想要修改數據的話,必須等待前面的線程釋放鎖後才被允許,這就利用了Java併發包中的CountDownLatch,CountDownLatch爲Java中的一個同步輔助類,在完成一組正在其他線程中進行的操作之前,它允許一個或多個線程一直等待。這裏,將線程數設置爲1,十分巧妙的實現了獨佔鎖的概念。
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章