HBase原生提供了主鍵索引,用戶能夠根據Rowkey進行高效的單行讀、前綴匹配、範圍查詢操做。但若須要使用屬性列進行查詢時,則只能使用filter在查詢範圍內進行逐行過濾。在掃描範圍較大時,會浪費大量的IO,請求RT也沒法保證。爲此,HBase加強版推出了原生二級索引來解決非Rowkey查詢的性能問題。
html
雲HBase加強版是基於阿里內部的HBase分支(亦稱Lindorm)構建的,二級索引是其核心能力之一,歷經多年雙11大考,在性能、吞吐、穩定性等方面都具有核心競爭力。算法
下面,咱們從一組示例出發來了解索引的使用及其能力。shell
從表設計和查詢設計的角度看,HBase加強版二級索引的使用與RDBMS的二級索引基本一致。下面咱們看一個簡單的示例:大學生信息表(Students),該表的主鍵(即rowkey)是學號,非主鍵是學生姓名和所屬的學院名稱。學生與學院是多對一的關係。數據庫
經過HBase shell建表,建索引:app
https://help.aliyun.com/document_detail/119572.html框架
# 建立主表student,列族名爲f
create 'student', 'f'
# 建立索引表department,爲department列建索引
# COVERED_ALL_COLUMNS是HBase加強版引入的新屬性關鍵字,
# 意味冗餘主表student中的全部列,以此來避免回查主表
create_index 'department', 'student', {INDEXED_COLUMNS => ['f:departement']}, {COVERED_COLUMNS => ['COVERED_ALL_COLUMNS']}
經過Java API進行數據訪問運維
// 定義一些常量
String id = "11"; // 一個隨機的學號
String studentName = "Harry";
String department = "CS";
byte[] f = Bytes.toBytes("f");
byte[] qStudent = Bytes.toBytes("name");
byte[] qDepartment = Bytes.toBytes("department");
// 寫入一個學生的數據
Put put = new Put(Bytes.toBytes(id));
put.addColumn(f, qStudent, Bytes.toBytes(studentName));
put.addColumn(f, qDepartment, Bytes.toBytes(department));
Table t = conn.getTable("student");
t.put(put); // put成功返回意味着主表和索引表都成功更新,變動當即可見
// 按department進行查詢
Scan scan = new Scan();
SingleColumnValueFilter where = new SingleColumnValueFilter(
f, qDepartment, EQUAL, Bytes.toBytes(department));
scan.setFilter(where);
ResultScanner rs = t.getScanner(scan);
// 處理查詢結果...
從上例可見,用HBase API直接描述查詢請求便可使用索引。HBase加強版會自動根據filter以及索引schema來匹配到最合適的索引進行查詢,必要時,在查完索引後也會回查主表(上例中,若是不是全冗餘索引,則會回查主表來補全列)。更多使用上的說明請參考二級索引開發手冊:異步
https://help.aliyun.com/document_detail/144577.html分佈式
HBase加強版二級索引的主要特性有:ide
支持爲單個主表建多個索引
支持單列和多列索引(組合索引)
支持冗餘索引:可顯式指定冗餘列,或冗餘全部列,避免回查主表的性能損耗
查詢優化:根據scan和filter自動選擇合適的索引表進行查詢,必要時會自動回查主表
online schema change:支持給已經在使用的表建索引,對主表讀寫無影響
支持TTL:索引表會自動繼承主表的TTL,主表和索引表數據一塊兒過時
支持自定義數據版本:用戶自定義數據時間戳寫入(暫未開放)
HBase加強版二級索引直接內置於內核中,並作了深度優化,提供了強大的吞吐與性能。下圖是HBase加強版二級索引與Apache Phoenix的全局索引的性能對比:
從上圖可見,不管RT仍是吞吐,HBase加強版二級索引均遠超Apache Phoenix。
數據寫入返回成功後,則索引數據可當即被讀到,消除傳統異步建索引方案中的數據延遲,提供具備必定程度的強一致性語義(主表和索引表的數據一致性),具體語義以下:
返回客戶端寫入成功,以後可當即讀到剛寫入的數據(包括主表和索引數據);寫入過程當中不保證同時可見
返回超時或IO出錯,則在一段時間內,該數據在主表和索引表中的可見性沒法肯定,但保證最終一致,即要麼全成功,要麼全不成功
HBase加強版提供的」寫成功後更新當即可見的語義「,可用於一些分佈式協同的任務,好比spark,在某些節點更新數據,另一些節點讀取上一輪計算寫入的數據。此時,必定能夠讀到剛寫入的數據。
全冗餘索引可完全避免回查主表,提高性能;同時也是語法糖,避免手工維護複雜的DDL。下面分別介紹。
若是查詢中須要的列在索引表中沒有,則查完索引後,還需回查主表。在分佈式場景下,回表查詢會使得查詢RT大幅度升高,最差狀況下可能會回查主表的所有region,訪問集羣中的全部機器。此時,索引帶來的性能收益已經無關緊要。經過精良的查詢設計和索引設計,咱們能夠在設計階段避免回查,但隨着業務發展變化,這個約束很難維持。所以,仍然須要冗餘索引(Covered Index)來解決。
HBase加強版創造性的引入了全冗餘索引的概念,即冗餘主表中的全部列,以此來完全避免回查主表。配合HBase的schema-free特性,主表中新增的任何列都會自動冗餘到索引表中。不管業務模式如何變化,都不須要回查主表。
同時,全冗餘也是可大幅度提高效率的語法糖,咱們能夠對好比下兩個SQL語句:
CREATE INDEX idx ON dt (c1, c2) include(c3, c4, c5, c6, ....);
CREATE INDEX idx ON dt (c1, c2) include(ALL);
對於大部分業務來講,表裏有數十列是常態,個別表可能會有數百列。若是爲了建冗餘索引,而把這數百列的列名再寫一遍,無疑是巨大的負擔(只能寫工具自動化作,人來作太容易出錯)。全冗餘索引的新語法給人工維護DDL提供了可能。
爲了得到上述兩點收益,全冗餘索引的代價是會佔用更多存儲空間。配合HBase加強版深度優化的ZStandard壓縮算法:
https://help.aliyun.com/document_detail/119549.html
可有效下降冗餘帶來存儲開銷。冷熱分離特性亦可應用於索引表,進一步控制成本。
對大部分場景來講,業務一行代碼不改就能用上索引。
從本文開頭的示例代碼中可見:
寫:寫主表便可,會自動同步到索引,強一致。用戶無需擔憂索引更新的問題
讀:基於主表進行查詢,直接按業務邏輯進行查詢表達,系統自動選擇合適的索引表進行查詢
這樣,用戶只需爲那些性能很差的查詢設計並添加索引,便可從索引特性中受益,實際的數據讀寫代碼通常不須要修改。同時,既有的HBase生態相關的產品,均可以無縫使用上索引。一些如Spring的框架軟件也可幫助用戶得到業務上的靈活性。
從一開始就設計好主表和所有索引幾乎是不可能的。所以,在後續業務發展過程當中,索引表可能須要不斷的刪除和新增。爲此,對一個已經有大量數據表添加、刪除索引,將是一個關鍵的運維操做。HBase加強版二級索引針對此場景作了特別的優化:
schema在線修改:索引的變動不影響主表的正常讀寫(就像一次普通的alter表操做),不影響其餘索引表
服務端rebuild:在服務端爲主錶的歷史數據構建索引
支持對超大主表添加索引:支持TB級別的主表添加新索引
流控:大主表的索引rebuild會消耗大量的系統資源,所以,精準的流控便可在兼顧索引構建速度的前提下,保障系統總體性能不會被影響
在有上述特性的加持下,索引變動的運維成本和風險大大下降,從容的適應業務發展。
HBase加強版二級索引是一種全局二級索引,每一個索引表都是一張獨立的HBase表。每張表的主鍵(rowkey)設計決定了其能支持的查詢模式。當同一份數據有多種rowkey組織時,就能支持多種查詢模式。這裏,主表和它的索引表,能夠看作是同一份數據的不一樣組織形式,各自可以高效的支持必定的查詢模式。
考慮本文開始時給出的學生信息表的示例:
主表Student:以學號(id)爲主鍵(rowkey),每行有兩列,學生姓名(name)和所屬的學院(department)。該設計僅支持按id進行查詢。若是用戶要按department或者name來查詢,須要全表掃描 + filter。在學生數較少時,這種暴力掃描徹底可行。但在數據量大時(數十萬乃至上億時),這種操做是沒法執行的。
爲了高效的支持按department查詢,可爲其創建一個全冗餘索引(使用HBase shell):
create_index 'department', 'student', {INDEXED_COLUMNS => ['f:departement']}, {COVERED_COLUMNS => ['COVERED_ALL_COLUMNS']}
在建索引時,系統會自動爲主表中的存量數據構建索引,寫入索引表中。主錶行和索引行是一一對應的。以後主表上發生的數據更新,也會自動同步給索引表。
考慮以下查詢:
-- 查找全部計算機學院(cs)的學生的姓名(name)
select name from student where department = 'cs';
這個查詢會直接命中索引表,按department列進行前綴匹配。從每個索引行中提取name字段,返回給客戶端。
若是咱們沒有創建冗餘索引,則索引表中不會存在name列。此時,在從索引表中讀取到學號(id)後,必須回查主表(三次按id的單行讀)來讀取name列。在分佈式場景下,10/12/13這三個id的數據可能分佈在三臺機器上。所以,回查主表最差狀況下須要3次RPC,加上查索引表的一次RPC,共需4次RPC。而若是是冗餘索引,則只需查索引表的一次RPC便可。
所以,在分佈式場景,尤爲是節點不少的大集羣,回查主錶帶來的性能損耗是巨大的(RT可能會增加數倍)。這也是咱們設計全冗餘索引的初衷:避免回查,提升性能。
數據只有被查詢才能創造價值,HBase原生高性能二級索引爲多維度查詢提供了一種有效的解決方案。在表設計上,用戶能夠參考MySQL等關係型數據庫的索引設計思路來進行HBase的索引設計。業務無需更改代碼,查詢優化可自動進行索引表的選擇。強一致、全冗餘索引等特性也有效下降了業務的使用門檻。
將來,咱們將對索引作進一步的優化和擴展,提供優質的用戶體驗。歡迎你們體驗HBase加強版。如您有對HBase相關的任何問題,歡迎經過釘釘與咱們聯繫(釘釘搜索「雲HBase值班」)。
HBase Java SDK
https://help.aliyun.com/document_detail/119568.html
HBase加強版Shell
https://help.aliyun.com/document_detail/119572.html
二級索引幫助文檔
https://help.aliyun.com/document_detail/144577.html
HBase 官方社區推薦必讀好文
HBase 原理|HBase 內存管理之 MemStore 進化論