# RegionServer 大小調整規則
Lars Hofhansl 寫了一篇關于 RegionServer 內存大小的[博客文章](http://hadoop-hbase.blogspot.com/2013/01/hbase-region-server-memory-sizing.html)。結果是您可能需要比您認為需要更多的內存。他深入研究了區域大小,memstore 大小,HDFS 復制因子以及其他要檢查的內容。
> 就個人而言,我會將每臺機器的最大磁盤空間放置在大約 6T左右來給HBase獨享,除非你的工作量非常大。在這種情況下,Java 堆應該是 32GB(20G 區域,128M 存儲器,其余默認值)。
- Lars Hofhansl <cite>[http://hadoop-hbase.blogspot.com/2013/01/hbase-region-server-memory-sizing.html](http://hadoop-hbase.blogspot.com/2013/01/hbase-region-server-memory-sizing.html)</cite>
## 36.關于列族的數量
HBase 目前不適用于兩個或三個列族以上的任何內容,因此請保持模式中的列族數量較少。目前,刷新和壓縮是基于每個區域進行的,因此如果一個列族承載了導致刷新的大部分數據,則即使它們承載的數據量很小,相鄰的族也將被刷新。當存在許多列族時,沖洗和壓縮交互可以產生一堆不必要的 i / o(通過改變刷新和壓縮以按列系列工作來解決)。有關壓縮的更多信息,請參見[壓縮](#compaction)。
如果可以在模式中使用,請嘗試使用一個列族。在數據訪問通常是列作用域的情況下,僅引入第二和第三列族;即您查詢一個列族或另一個列族,但通常不是同時查詢兩個列族。
### 36.1。 ColumnFamilies 的基數
如果單個表中存在多個 ColumnFamilies,請注意基數(即行數)。如果 ColumnFamilyA 有 100 萬行而 ColumnFamilyB 有 10 億行,則 ColumnFamilyA 的數據可能會分布在許多區域(和 RegionServers)中。這使得 ColumnFamilyA 的大規模掃描效率降低。
## 37\. Rowkey Design
### 37.1。 Hotspotting
HBase 中的行按行按字典順序排序。此設計優化了掃描,允許您將相關的行或將要一起讀取的行存儲在彼此附近。但是,設計不良的行鍵是 _ 熱點 _ 的常見來源。當大量客戶端流量指向群集的一個節點或僅幾個節點時,就會發生熱點。此流量可能表示讀取,寫入或其他操作。流量超過負責托管該區域的單個機器,導致性能下降并可能導致區域不可用。這也可能對同一區域服務器托管的其他區域產生負面影響,因為該主機無法為請求的負載提供服務。設計數據訪問模式非常重要,這樣才能完全均勻地利用集群。
為了防止寫入熱點,設計行鍵使得真正需要位于同一區域的行位于同一區域中,但從更大的角度來看,數據正在跨集群被寫入群集中的多個區域,而不是一次一個。下面描述了一些避免熱點的常用技術,以及它們的一些優點和缺點。
鹽
在這種意義上,Salting 與加密無關,但是指的是將隨機數添加到行鍵的開頭。在這種情況下,salting 指的是向行鍵添加隨機分配的前綴,以使其排序與其原來的方式不同。可能的前綴數對應于您希望跨數據傳播的區域數。如果您有一些“熱”行鍵模式在其他更均勻分布的行中反復出現,則 Salting 可能會有所幫助。請考慮以下示例,該示例顯示 salting 可以在多個 RegionServers 之間傳播寫入負載,并說明了對讀取的一些負面影響。
實施例 11.加鹽實施例
假設您有以下行鍵列表,并且您的表被拆分,以便字母表中的每個字母都有一個區域。前綴“a”是一個區域,前綴“b”是另一個區域。在此表中,以“f”開頭的所有行都在同一區域中。此示例關注具有以下鍵的行:
```
foo0001
foo0002
foo0003
foo0004
```
現在,想象一下,您希望將它們分布在四個不同的地區。您決定使用四種不同的鹽:`a`,`b`,`c`和`d`。在這種方案下,這些字母前綴中的每一個都將位于不同的區域。應用鹽后,您將改為使用以下 rowkeys。既然你現在可以寫入四個不同的區域,理論上你在寫入時的吞吐量是所有寫入到同一區域時的四倍。
```
a-foo0003
b-foo0001
c-foo0004
d-foo0002
```
然后,如果添加另一行,將隨機分配四個可能的鹽值中的一個,并最終靠近其中一個現有行。
```
a-foo0003
b-foo0001
c-foo0003
c-foo0004
d-foo0002
```
由于此分配是隨機的,因此如果要按字典順序檢索行,則需要做更多工作。通過這種方式,salting 嘗試增加寫入的吞吐量,但在讀取期間會產生成本。
哈希
您可以使用單向 _ 散列 _ 而不是隨機分配,這將導致給定行始終使用相同的前綴“加鹽”,從而將負載分散到 RegionServers,但是允許讀取期間的可預測性。使用確定性哈希允許客戶端重建完整的 rowkey 并使用 Get 操作正常檢索該行。
示例 12.散列示例
在上面的 salting 示例中給出相同的情況,您可以改為應用單向散列,該散列將導致具有鍵`foo0003`的行始終并且可預測地接收`a`前綴。然后,要檢索該行,您就已經知道了該密鑰。您還可以優化事物,以便某些鍵對始終位于同一區域,
反轉鍵
防止熱點的第三個常見技巧是反轉固定寬度或數字行鍵,以便最常更改(最低有效數字)的部分是第一個。這有效地使行鍵隨機化,但犧牲了行排序屬性。
參見 [https://communities.intel.com/community/itpeernetwork/datastack/blog/2013/11/10/discussion-on-designing-hbase-tables](https://communities.intel.com/community/itpeernetwork/datastack/blog/2013/11/10/discussion-on-designing-hbase-tables) 和[關于鹽漬表的文章[來自 Phoenix 項目的 HTG3]以及](https://phoenix.apache.org/salted.html) [HBASE-11682](https://issues.apache.org/jira/browse/HBASE-11682) 的評論中有關避免熱點的更多信息的討論。
### 37.2。單調遞增行鍵/時間序列數據
在 Tom White 的書 [Hadoop:The Definitive Guide](http://oreilly.com/catalog/9780596521981) (O'Reilly)的 HBase 章節中,有一個優化注釋,用于觀察導入過程與所有客戶在音樂會中鎖定步驟的現象敲擊表中的一個區域(因此,單個節點),然后移動到下一個區域,等等。單調增加行鍵(即使用時間戳),這也將發生。看看 IKai Lan 的這個漫畫,為什么在類似 BigTable 的數據存儲中單調增加行鍵是有問題的:[單調遞增值很差](http://ikaisays.com/2011/01/25/app-engine-datastore-tip-monotonically-increasing-values-are-bad/)。通過將輸入記錄隨機化為不按排序順序,可以減輕由單調增加的密鑰引起的單個區域的堆積,但通常最好避免使用時間戳或序列(例如,1,2,3)作為行鍵。
如果你確實需要將時間序列數據上傳到 HBase,你應該學習 [OpenTSDB](http://opentsdb.net/) 作為一個成功的例子。它有一個描述它在 HBase 中使用的[模式](http://opentsdb.net/schema.html)的頁面。 OpenTSDB 中的關鍵格式實際上是[metric_type] [event_timestamp],乍一看似乎與先前關于不使用時間戳作為關鍵字的建議相矛盾。然而,不同之處在于時間戳不在密鑰的 _ 前導 _ 位置,并且設計假設是存在數十或數百(或更多)不同的度量類型。因此,即使具有混合度量類型的連續輸入數據流,Puts 也分布在表中的各個區域點上。
有關一些 rowkey 設計示例,請參見 [schema.casestudies](#schema.casestudies) 。
### 37.3。盡量減少行和列的大小
在 HBase 中,值總是用它們的坐標運算;當一個單元格值通過系統時,它總是將伴隨著它的行,列名和時間戳 。如果您的行和列名稱很大,特別是與單元格值的大小相比,那么您可能會遇到一些有趣的場景。其中一個是 Marc Limotte 在 [HBASE-3551](https://issues.apache.org/jira/browse/HBASE-3551?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13005272#comment-13005272) 尾部描述的情況(推薦!)。其中,為了便于隨機訪問而保留在 HBase 存儲文件( [StoreFile(HFile)](#hfile))上的索引可能最終占用 HBase 分配的 RAM 的大塊,因為單元值坐標很大。上面引用的注釋中的標記建議增加塊大小,以便存儲文件索引中的條目以更大的間隔發生,或者修改表模式,從而使得行和列名稱更小。壓縮也將使更大的指數。在用戶郵件列表中查看主題[問題 storefileIndexSize](http://search-hadoop.com/m/hemBv1LiN4Q1/a+question+storefileIndexSize&subj=a+question+storefileIndexSize) 。
大多數情況下,小的低效率并不重要。不幸的是,這是他們這樣做的情況。無論為 ColumnFamilies,屬性和行鍵選擇何種模式,它們都可以在數據中重復數十億次。
有關 HBase 在內部存儲數據的詳細信息,請參閱 [keyvalue](#keyvalue) ,了解其重要性。
#### 37.3.1。列族
盡量保持 ColumnFamily 名稱盡可能小,最好是一個字符(例如“d”表示數據/默認值)。
有關 HBase 在內部存儲數據的詳細信息,請參閱 [KeyValue](#keyvalue) 以了解其重要性。
#### 37.3.2。屬性
盡管詳細的屬性名稱(例如,“myVeryImportantAttribute”)更易于閱讀,但是更喜歡較短的屬性名稱(例如,“via”)來存儲在 HBase 中。
See [keyvalue](#keyvalue) for more information on HBase stores data internally to see why this is important.
#### 37.3.3。 Rowkey 長度
保持它們盡可能短,以便它們仍然可用于所需的數據訪問(例如 Get vs. Scan)。對數據訪問無用的短鍵并不比具有更好的 get / scan 屬性的更長鍵更好。在設計 rowkeys 時要權衡取舍。
#### 37.3.4。字節模式
長是 8 個字節。您可以在這八個字節中存儲最多 18,446,744,073,709,551,615 的無符號數。如果將此數字存儲為字符串 - 假設每個字符有一個字節 - 則需要將近 3 倍的字節。
不相信?下面是一些您可以自己運行的示例代碼。
```
// long
//
long l = 1234567890L;
byte[] lb = Bytes.toBytes(l);
System.out.println("long bytes length: " + lb.length); // returns 8
String s = String.valueOf(l);
byte[] sb = Bytes.toBytes(s);
System.out.println("long as string length: " + sb.length); // returns 10
// hash
//
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(Bytes.toBytes(s));
System.out.println("md5 digest bytes length: " + digest.length); // returns 16
String sDigest = new String(digest);
byte[] sbDigest = Bytes.toBytes(sDigest);
System.out.println("md5 digest as string length: " + sbDigest.length); // returns 26
```
不幸的是,使用類型的二進制表示將使您的數據更難在代碼之外讀取。例如,這是在增加值時在 shell 中看到的內容:
```
hbase(main):001:0> incr 't', 'r', 'f:q', 1
COUNTER VALUE = 1
hbase(main):002:0> get 't', 'r'
COLUMN CELL
f:q timestamp=1369163040570, value=\x00\x00\x00\x00\x00\x00\x00\x01
1 row(s) in 0.0310 seconds
```
shell 盡最大努力打印一個字符串,在這種情況下,它決定只打印十六進制。區域名稱中的行鍵也會發生相同的情況。如果你知道存儲了什么就可以了,但如果可以將任意數據放在同一個單元格中,它也可能是不可讀的。這是主要的權衡。
### 37.4。反向時間戳
> 反向掃描 AP
>
> [HBASE-4811](https://issues.apache.org/jira/browse/HBASE-4811) 實現了一個 API 來反向掃描表格或表格中的范圍,從而減少了優化模式以進行正向或反向掃描的需要。此功能在 HBase 0.98 及更高版本中可用。有關詳細信息,請參閱 [Scan.setReversed()](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Scan.html#setReversed-boolean-)。
數據庫處理中的一個常見問題是快速找到最新版本的值。使用反向時間戳作為密鑰的一部分的技術可以極大地幫助解決此問題的特殊情況。同樣可以在 Tom White 的書 Hadoop:The Definitive Guide(O'Reilly)的 HBase 章節中找到,該技術涉及將(`Long.MAX_VALUE - timestamp`)附加到任何鍵的末尾,例如: [鍵] [reverse_timestamp]。
通過執行 Scan [key]并獲取第一條記錄,可以找到表中[key]的最新值。由于 HBase 鍵是按排序順序排列的,因此該鍵在[key]的任何舊行鍵之前排序,因此是第一個。
將使用此技術而不是使用[版本號](#schema.versions),其意圖是“永久”(或很長時間)保留所有版本,同時通過使用快速獲取對任何其他版本的訪問權限相同的掃描技術。
### 37.5。 Rowkeys 和 ColumnFamilies
Rowkeys 的作用域為 ColumnFamilies。因此,在沒有沖突的表中存在的每個 ColumnFamily 中可以存在相同的 rowkey。
### 37.6。 Rowkeys 的不變性
Rowkeys 無法更改。它們可以在表中“更改”的唯一方法是刪除行然后重新插入。這是關于 HBase dist-list 的一個相當常見的問題,因此第一次(和/或在插入大量數據之前)使 rowkeys 正確是必要的。
### 37.7。 RowKeys 與區域分裂的關系
如果你預分割你的表,那么 _ 關鍵 _ 就可以理解你的 rowkey 如何在區域邊界上分布。作為為什么這很重要的一個例子,考慮使用可顯示的十六進制字符作為密鑰的前導位置的例子(例如,“0000000000000000”到“fffffffffffffffff”)。通過`Bytes.split`運行這些鍵范圍(這是在`Admin.createTable(byte[] startKey, byte[] endKey, numRegions)`中為 10 個區域創建區域時使用的拆分策略將生成以下拆分...
```
48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 // 0
54 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 -10 // 6
61 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -67 -68 // =
68 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -124 -126 // D
75 75 75 75 75 75 75 75 75 75 75 75 75 75 75 72 // K
82 18 18 18 18 18 18 18 18 18 18 18 18 18 18 14 // R
88 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -40 -44 // X
95 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -97 -102 // _
102 102 102 102 102 102 102 102 102 102 102 102 102 102 102 102 // f
```
(注意:前導字節在右側列為注釋。)鑒于第一次拆分是'0'而最后一次拆分是'f',一切都很好,對吧?沒那么快。
問題是所有數據都將堆積在前 2 個區域和最后一個區域,從而產生“塊狀”(可能是“熱”)區域問題。要了解原因,請參閱 [ASCII 表](http://www.asciitable.com)。 '0'是字節 48,'f'是字節 102,但字節值(字節 58 到 96)之間存在巨大差距,_ 永遠不會出現在此鍵空間 _ 中,因為唯一的值是[0 -9]和[af]。因此,中間區域永遠不會被使用。為了使用該示例鍵空間進行預分割工作,需要自定義分割(即,不依賴于內置分割方法)。
第 1 課:預分割表通常是最佳實踐,但您需要預先拆分它們,以便在密鑰空間中可以訪問所有區域。雖然此示例演示了十六進制鍵空間的問題,但 _ 任何 _ 鍵空間都會出現同樣的問題。了解您的數據。
第 2 課:盡管通常不可取,但只要在密鑰空間中可以訪問所有創建的區域,使用十六進制密鑰(更常見的是可顯示的數據)仍可以使用預分割表。
總結此示例,以下是如何為十六進制密鑰預先創建適當的拆分的示例:
```
public static boolean createTable(Admin admin, HTableDescriptor table, byte[][] splits)
throws IOException {
try {
admin.createTable( table, splits );
return true;
} catch (TableExistsException e) {
logger.info("table " + table.getNameAsString() + " already exists");
// the table already exists...
return false;
}
}
public static byte[][] getHexSplits(String startKey, String endKey, int numRegions) {
byte[][] splits = new byte[numRegions-1][];
BigInteger lowestKey = new BigInteger(startKey, 16);
BigInteger highestKey = new BigInteger(endKey, 16);
BigInteger range = highestKey.subtract(lowestKey);
BigInteger regionIncrement = range.divide(BigInteger.valueOf(numRegions));
lowestKey = lowestKey.add(regionIncrement);
for(int i=0; i < numRegions-1;i++) {
BigInteger key = lowestKey.add(regionIncrement.multiply(BigInteger.valueOf(i)));
byte[] b = String.format("%016x", key).getBytes();
splits[i] = b;
}
return splits;
}
```
## 38.版本號
### 38.1。最大版本數
通過 [HColumnDescriptor](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/HColumnDescriptor.html) 按列系列配置要存儲的最大行版本數。最大版本的默認值為 1.這是一個重要的參數,因為如[數據模型](#datamodel)部分所述 HBase 執行 _ 而不是 _ 覆蓋行值,而是每行按時間存儲不同的值(和預選值)。在主要壓縮過程中刪除了多余的版本。根據應用需求,可能需要增加或減少最大版本的數量。
不建議將最大版本的數量設置為非常高的級別(例如,數百或更多),除非這些舊值非常珍貴,因為這將大大增加 StoreFile 的大小。
### 38.2。最小版本數
與最大行版本數一樣,通過 [HColumnDescriptor](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/HColumnDescriptor.html) 按列系列配置要保留的最小行版本數。最小版本的默認值為 0,表示該功能已禁用。行版本參數的最小數量與生存時間參數一起使用,并且可以與行版本參數的數量組合以允許配置,例如“保留最后 T 分鐘的數據,最多 N 個版本,_ 但至少保持 _ 的 M 個版本“(其中 M 是最小行數版本的值,M < N)僅當為列族啟用生存時間且必須小于行版本數時,才應設置此參數。
## 39.支持的數據類型
HBase 通過 [Put](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Put.html) 和 [Result](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Result.html) 支持“字節輸入/字節輸出”接口,因此任何可以轉換為字節數組的都可以存儲為值。輸入可以是字符串,數字,復雜對象,甚至是圖像,只要它們可以呈現為字節。
值的大小存在實際限制(例如,在 HBase 中存儲 10-50MB 對象可能要求太多);在郵件列表中搜索有關此主題的對話。 HBase 中的所有行都符合[數據模型](#datamodel),包括版本控制。在進行設計時要考慮到這一點,以及列族的塊大小。
### 39.1。計數器
值得特別提及的一種受支持的數據類型是“計數器”(即,能夠進行數字的原子增量)。參見`Table`中的[增量](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Table.html#increment%28org.apache.hadoop.hbase.client.Increment%29)。
計數器上的同步在 RegionServer 上完成,而不是在客戶端中完成。
## 40.加入
如果您有多個表,請不要忘記將 [Joins](#joins) 的可能性考慮到架構設計中。
## 41.生存時間(TTL)
列族可以設置 TTL 長度(以秒為單位),HBase 將在到達到期時間后自動刪除行。這適用于行的所有版本 - 即使是當前版本。在 HBase 中為行編碼的 TTL 時間以 UTC 指定。
在次要壓縮時刪除僅包含過期行的存儲文件。將`hbase.store.delete.expired.storefile`設置為`false`將禁用此功能。將最小版本數設置為 0 以外也會禁用此功能。
有關詳細信息,請參閱 [HColumnDescriptor](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/HColumnDescriptor.html) 。
最新版本的 HBase 還支持按每個單元格設置生存時間。有關詳細信息,請參閱 [HBASE-10560](https://issues.apache.org/jira/browse/HBASE-10560) 。使用 Mutation#setTTL 將細胞 TTL 作為突變請求(Appends,Increments,Puts 等)的屬性提交。如果設置了 TTL 屬性,則它將應用于操作在服務器上更新的所有單元。單元格 TTL 處理和 ColumnFamily TTL 之間存在兩個顯著差異:
* 單元格 TTL 以毫秒而不是秒為單位表示。
* 單元格 TTL 不能將單元格的有效生存期延長到超出ColumnFamily級別的TTL設置。
## 42.保留被刪除的單元格
默認情況下,刪除標記會延伸回到時間的開頭。因此,[Get](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Get.html) 或 [Scan](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Scan.html)操作也不會看到已刪除的單元格(行或列),甚至即使獲取或掃描操作指示放置刪除標記之前的時間范圍。
ColumnFamilies 可以選擇保留已刪除的單元格。在這種情況下,仍然可以檢索已刪除的單元格,只要這些操作指定的時間范圍在任何將影響單元格的刪除的時間戳之前結束。即使存在刪除,這也允許進行時間點查詢。
刪除的單元格仍然受 TTL 限制,并且永遠不會有超過“最大版本號”的已刪除單元格。新的“原始”掃描選項將返回所有已刪除的行和刪除標記。
使用 HBase Shell 更改`KEEP_DELETED_CELLS`的值
```
hbase> hbase> alter ‘t1′, NAME => ‘f1′, KEEP_DELETED_CELLS => true
```
示例 13.使用 API??更改`KEEP_DELETED_CELLS`的值
```
...
HColumnDescriptor.setKeepDeletedCells(true);
...
```
讓我們說明在表上設置`KEEP_DELETED_CELLS`屬性的基本效果。
首先,沒有:
```
create 'test', {NAME=>'e', VERSIONS=>2147483647}
put 'test', 'r1', 'e:c1', 'value', 10
put 'test', 'r1', 'e:c1', 'value', 12
put 'test', 'r1', 'e:c1', 'value', 14
delete 'test', 'r1', 'e:c1', 11
hbase(main):017:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
r1 column=e:c1, timestamp=11, type=DeleteColumn
r1 column=e:c1, timestamp=10, value=value
1 row(s) in 0.0120 seconds
hbase(main):018:0> flush 'test'
0 row(s) in 0.0350 seconds
hbase(main):019:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
r1 column=e:c1, timestamp=11, type=DeleteColumn
1 row(s) in 0.0120 seconds
hbase(main):020:0> major_compact 'test'
0 row(s) in 0.0260 seconds
hbase(main):021:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
1 row(s) in 0.0120 seconds
```
注意如何釋放刪除單元格。
現在讓我們只在表上設置`KEEP_DELETED_CELLS`的情況下運行相同的測試(你可以做 table 或 per-column-family):
```
hbase(main):005:0> create 'test', {NAME=>'e', VERSIONS=>2147483647, KEEP_DELETED_CELLS => true}
0 row(s) in 0.2160 seconds
=> Hbase::Table - test
hbase(main):006:0> put 'test', 'r1', 'e:c1', 'value', 10
0 row(s) in 0.1070 seconds
hbase(main):007:0> put 'test', 'r1', 'e:c1', 'value', 12
0 row(s) in 0.0140 seconds
hbase(main):008:0> put 'test', 'r1', 'e:c1', 'value', 14
0 row(s) in 0.0160 seconds
hbase(main):009:0> delete 'test', 'r1', 'e:c1', 11
0 row(s) in 0.0290 seconds
hbase(main):010:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
r1 column=e:c1, timestamp=11, type=DeleteColumn
r1 column=e:c1, timestamp=10, value=value
1 row(s) in 0.0550 seconds
hbase(main):011:0> flush 'test'
0 row(s) in 0.2780 seconds
hbase(main):012:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
r1 column=e:c1, timestamp=11, type=DeleteColumn
r1 column=e:c1, timestamp=10, value=value
1 row(s) in 0.0620 seconds
hbase(main):013:0> major_compact 'test'
0 row(s) in 0.0530 seconds
hbase(main):014:0> scan 'test', {RAW=>true, VERSIONS=>1000}
ROW COLUMN+CELL
r1 column=e:c1, timestamp=14, value=value
r1 column=e:c1, timestamp=12, value=value
r1 column=e:c1, timestamp=11, type=DeleteColumn
r1 column=e:c1, timestamp=10, value=value
1 row(s) in 0.0650 seconds
```
KEEP_DELETED_CELLS 是為了避免在僅移除它們的原因是刪除標記時從 HBase 中刪除 Cell。因此,如果啟用了 KEEP_DELETED_CELLS,則刪除的單元格將被刪除,如果您編寫的版本多于配置的最大版本,或者您有 TTL 且單元格超出配置的超時等。
## 43.二級索引和備用查詢路徑
這部分也可以標題為“-如果我的表 rowkey 看起來像這個,但我也想像那樣查詢我的表-。” dist-list 上的一個常見示例是行密鑰的格式為“user-timestamp”,但對于某些時間范圍的用戶活動有報告要求。因此,用戶選擇很容易,因為它處于鍵的引導位置,但時間不是。
關于處理這個問題的最佳方法沒有一個答案,因為它取決于......
* 用戶數量
* 數據大小和數據到達率
* 報告要求的靈活性(例如,完全臨時日期選擇與預配置范圍)
* 期望的查詢執行速度(例如,對于某些特定報告,90 秒可能是合理的,而對于其他人來說可能太長)
并且解決方案也受到群集大小以及解決方案需要多少處理能力的影響。常用技術在下面的小節中。這是一個全面但并非詳盡無遺的方法清單。
二級索引需要額外的集群空間和處理應該不足為奇。這正是 RDBMS 中發生的情況,因為創建備用索引的行為需要更新空間和處理周期。在這方面,RDBMS 產品更先進,可以開箱即用地處理備用索引管理。但是,HBase 在更大的數據量下可以更好地擴展,因此這是一項功能權衡。
在實施任何這些方法時,請注意 [Apache HBase 性能調整](#performance)。
另外,請看這個 dist-list 線程中的 David Butler 響應 [HBase,mail#user - Stargate + hbase](http://search-hadoop.com/m/nvbiBp2TDP/Stargate%252Bhbase&subj=Stargate+hbase)
### 43.1。過濾查詢
根據具體情況,使用[客戶端請求過濾器](#client.filter)可能是合適的。在這種情況下,不會創建二級索引。但是,不要嘗試從應用程序(即單線程客戶端)對這樣的大型表進行全掃描。
### 43.2。定期更新二級索引
可以在另一個表中創建二級索引,該表是通過MapReduce作業定期更新。該作業可以在一天之內執行,但根據加載策略,它仍可能與主數據表不同步。
有關詳細信息,請參閱 [mapreduce.example.readwrite](#mapreduce.example.readwrite) 。
### 43.3。雙寫二級索引
另一種策略是在向集群發布數據時構建二級索引(例如,寫入數據表,寫入索引表)。如果在數據表已經存在之后采用這種方法,那么使用 MapReduce 作業的二級索引將需要自舉(參見 [secondary.indexes.periodic](#secondary.indexes.periodic) )。
### 43.4。匯總表
如果時間范圍非常寬(例如,一年的報告)并且數據量很大,則匯總表是一種常用方法。這些將通過 MapReduce 作業生成到另一個表中。
有關詳細信息,請參閱 [mapreduce.example.summary](#mapreduce.example.summary) 。
### 43.5。協處理器二級索引
協處理器就像 RDBMS 觸發器一樣。這些是在 0.92 中添加的。有關更多信息,請參閱[協處理器](#cp)
## 44.制約因素
HBase 目前支持傳統(SQL)數據庫用語中的“約束”。約束的建議用法是強制執行表中屬性的業務規則(例如,確保值在 1-10 范圍內)。約束也可用于強制引用完整性,但強烈建議不要這樣做,因為它會顯著降低啟用完整性檢查的表的寫入吞吐量。自版本 0.94 起,可以在 [Constraint](https://hbase.apache.org/devapidocs/org/apache/hadoop/hbase/constraint/Constraint.html) 中找到有關使用約束的詳細文檔。
## 45.模式設計案例研究
下面將介紹 HBase 的一些典型數據攝取用例,以及如何處理 rowkey 設計和構造。注意:這只是潛在方法的說明,而不是詳盡的清單。了解您的數據,了解您的處理要求。
在閱讀這些案例研究之前,強烈建議您先閱讀 [HBase 和 Schema Design](#schema) 的其余部分。
描述了以下案例研究:
* 記錄數據/時間序列數據
* 在類固醇上記錄數據/時間序列
* 顧客訂單
* 高/寬/中圖式設計
* 列表數據
### 45.1。案例研究 - 日志數據和時間序列數據
假設正在收集以下數據元素。
* 主機名
* 時間戳
* 記錄事件
* 值/消息
我們可以將它們存儲在名為 LOG_DATA 的 HBase 表中,但是 rowkey 是什么?從這些屬性中,rowkey 將是 hostname,timestamp 和 log-event 的某種組合 - 但具體是什么?
#### 45.1.1。 Rowkey 主位的時間戳
rowkey `[timestamp][hostname][log-event]`遭受[單調遞增行鍵/時間序列數據](#timeseries)中描述的單調遞增的 rowkey 問題。
通過對時間戳執行 mod 操作,在 dist-lists 中經常提到的關于“bucketing”時間戳的另一種模式。如果面向時間的掃描很重要,這可能是一種有用的方法。必須注意桶的數量,因為這將需要相同數量的掃描才能返回結果。
```
long bucket = timestamp % numBuckets;
```
構建:
```
[bucket][timestamp][hostname][log-event]
```
如上所述,要選擇特定時間范圍的數據,需要為每個存儲桶執行掃描。例如,100 個存儲桶將在密鑰空間中提供廣泛的分布,但是需要 100 個掃描才能獲得單個時間戳的數據,因此需要權衡利弊。
#### 45.1.2。主機在 Rowkey 主位
如果存在大量主機以在密鑰空間上傳播寫入和讀取,則 rowkey `[hostname][log-event][timestamp]`是候選者。如果按主機名掃描是優先事項,則此方法很有用。
#### 45.1.3。時間戳或反向時間戳?
如果最重要的訪問路徑是拉取最近的事件,那么將時間戳存儲為反向時間戳(例如,`timestamp = Long.MAX_VALUE – timestamp`)將創建能夠在`[hostname][log-event]`上執行掃描以獲取最近捕獲的事件的屬性。
這兩種方法都不對,它只取決于最適合這種情況的方法。
> Reverse Scan AP
>
> [HBASE-4811](https://issues.apache.org/jira/browse/HBASE-4811) implements an API to scan a table or a range within a table in reverse, reducing the need to optimize your schema for forward or reverse scanning. This feature is available in HBase 0.98 and later. See [Scan.setReversed()](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Scan.html#setReversed-boolean-) for more information.
#### 45.1.4。可變長度或固定長度的 Rowkeys?
重要的是要記住,在 HBase 的每一列上都標記了 rowkeys。如果主機名是`a`且事件類型是`e1`,則生成的 rowkey 將非常小。但是,如果攝取的主機名是`myserver1.mycompany.com`而事件類型是`com.package1.subpackage2.subsubpackage3.ImportantService`,這個怎么辦?
在 rowkey 中使用一些替換可能是有意義的。至少有兩種方法:散列和數字。在 Rowkey Lead Position 示例中的 Hostname 中,它可能如下所示:
帶哈希的復合 Rowkey:
* [主機名的 MD5 哈希值] = 16 個字節
* [事件類型的 MD5 哈希] = 16 個字節
* [timestamp] = 8 個字節
具有數字替換的復合 Rowkey:
對于這種方法,除了 LOG_DATA 之外,還需要另一個查找表,稱為 LOG_TYPES。 LOG_TYPES 的 rowkey 是:
* `[type]`(例如,表示主機名與事件類型的字節)
* 原始主機名或事件類型的`[bytes]`可變長度字節。
此 rowkey 的列可以是帶有指定編號的 long,可以通過使用 [HBase 計數器](https://hbase.apache.org/apidocs/org/apache/hadoop/hbase/client/Table.html#incrementColumnValue-byte:A-byte:A-byte:A-long-)獲得
因此生成的復合 rowkey 將是:
* [取代主機名長] = 8 個字節
* [代替事件類型的長度] = 8 個字節
* [timestamp] = 8 bytes
在散列或數字替換方法中,hostname 和 event-type 的原始值可以存儲為列。
### 45.2。案例研究 - 類固醇的日志數據和時間序列數據
這實際上是 OpenTSDB 方法。 OpenTSDB 所做的是重寫數據并將行打包到特定時間段的列中。有關詳細說明,請參閱: [http://opentsdb.net/schema.html](http://opentsdb.net/schema.html) 和[來自 HBaseCon2012 的 OpenTSDB](https://www.slideshare.net/cloudera/4-opentsdb-hbasecon) 的經驗教訓。
但這就是一般概念的工作原理:例如以這種方式攝取數據......
```
[hostname][log-event][timestamp1]
[hostname][log-event][timestamp2]
[hostname][log-event][timestamp3]
```
每個詳細事件都有單獨的 rowkeys,但是會像這樣重寫...
```
[hostname][log-event][timerange]
```
并且每個上述事件被轉換成以相對于開始時間范圍的時間偏移(例如,每 5 分鐘)存儲的列。這顯然是一種非常先進的處理技術,但 HBase 使這成為可能。
### 45.3。案例研究 - 客戶/訂單
假設 HBase 用于存儲客戶和訂單信息。攝取了兩種核心記錄類型:客戶記錄類型和訂單記錄類型。
客戶記錄類型將包括您通常期望的所有內容:
* 顧客號碼
* 顧客姓名
* 地址(例如,城市,州,郵編)
* 電話號碼等
訂單記錄類型包括以下內容:
* Customer number
* 訂單號
* 銷售日期
* 用于裝運位置和項目的一系列嵌套對象(有關詳細信息,請參閱[訂單對象設計](#schema.casestudies.custorder.obj))
假設客戶編號和銷售訂單的組合唯一地標識訂單,這兩個屬性將組成 rowkey,特別是組合鍵,例如:
```
[customer number][order number]
```
對于 ORDER 表。但是,還有更多的設計決策要做: _raw_ 值是 rowkeys 的最佳選擇嗎?
日志數據用例中的相同設計問題在這里面對我們。什么是客戶編號的密鑰空間,以及格式是什么(例如,數字?字母數字?)因為在 HBase 中使用固定長度密鑰是有利的,以及可以支持密鑰空間中合理傳播的密鑰,類似選項出現:
Composite Rowkey With Hashes:
* [客戶編號的 MD5] = 16 個字節
* [訂貨號的 MD5] = 16 個字節
復合數字/哈希組合 Rowkey:
* [代替客戶編號] = 8 個字節
* [MD5 of order number] = 16 bytes
#### 45.3.1。單表?多個表?
傳統的設計方法將為 CUSTOMER 和 SALES 提供單獨的表。另一種選擇是將多種記錄類型打包到一個表中(例如,CUSTOMER ++)。
客戶記錄類型 Rowkey:
* [顧客 ID]
* [type] =表示客戶記錄類型為“1”的類型
訂單記錄類型 Rowkey:
* [customer-id]
* [type] =表示訂單記錄類型的“2”的類型
* [訂購]
這種特殊的 CUSTOMER ++方法的優點是可以按客戶 ID 組織許多不同的記錄類型(例如,單次掃描可以獲得有關該客戶的所有信息)。缺點是掃描特定記錄類型并不容易。
#### 45.3.2。訂單對象設計
現在我們需要解決如何為 Order 對象建模。假設類結構如下:
訂購
(訂單可以有多個 ShippingLocations
的 LineItem
(ShippingLocation 可以有多個 LineItems
存儲此數據有多種選擇。
##### 完全標準化
使用這種方法,ORDER,SHIPPING_LOCATION 和 LINE_ITEM 會有單獨的表。
ORDER 表的 rowkey 如上所述: [schema.casestudies.custorder](#schema.casestudies.custorder)
SHIPPING_LOCATION 的復合 rowkey 是這樣的:
* `[order-rowkey]`
* `[shipping location number]`(例如,第 1 位,第 2 位等)
LINE_ITEM 表的復合 rowkey 將是這樣的:
* `[order-rowkey]`
* `[shipping location number]` (e.g., 1st location, 2nd, etc.)
* `[line item number]`(例如,第 1 個 lineitem,第 2 個等)
這樣的規范化模型很可能是使用 RDBMS 的方法,但這不是您使用 HBase 的唯一選擇。這種方法的缺點是,要檢索有關任何訂單的信息,您將需要:
* 獲取訂單的 ORDER 表
* 在 SHIPPING_LOCATION 表上掃描該訂單以獲取 ShippingLocation 實例
* 在 LINE_ITEM 上掃描每個 ShippingLocation
無論如何,這就是 RDBMS 所做的事情,但由于 HBase 中沒有連接,你只是更加意識到這一事實。
##### 單表與記錄類型
使用這種方法,將存在一個包含的單個表 ORDER
Order rowkey 如上所述: [schema.casestudies.custorder](#schema.casestudies.custorder)
* `[order-rowkey]`
* `[ORDER record type]`
ShippingLocation 復合 rowkey 將是這樣的:
* `[order-rowkey]`
* `[SHIPPING record type]`
* `[shipping location number]` (e.g., 1st location, 2nd, etc.)
LineItem 復合 rowkey 將是這樣的:
* `[order-rowkey]`
* `[LINE record type]`
* `[shipping location number]` (e.g., 1st location, 2nd, etc.)
* `[line item number]` (e.g., 1st lineitem, 2nd, etc.)
##### 反規范化
具有記錄類型的單表方法的變體是對一些對象層次結構進行非規范化和展平,例如將 ShippingLocation 屬性折疊到每個 LineItem 實例上。
The LineItem composite rowkey would be something like this:
* `[order-rowkey]`
* `[LINE record type]`
* `[line item number]`(例如,第 1 個 lineitem,第 2 個等,必須注意整個訂單中有唯一性)
而 LineItem 列將是這樣的:
* 項目編號
* 數量
* 價錢
* shipToLine1(從 ShippingLocation 非規范化)
* shipToLine2(從 ShippingLocation 非規范化)
* shipToCity(從 ShippingLocation 非規范化)
* shipToState(從 ShippingLocation 非規范化)
* shipToZip(從 ShippingLocation 非規范化)
這種方法的優點包括不太復雜的對象層次結構,但其中一個缺點是,如果任何此類信息發生更改,更新會變得更加復雜。
##### 對象 BLOB
使用這種方法,整個 Order 對象圖以某種方式被視為 BLOB。例如,ORDER 表的 rowkey 如上所述: [schema.casestudies.custorder](#schema.casestudies.custorder) ,一個名為“order”的列將包含一個可以反序列化的對象,該對象包含容器 Order,ShippingLocations 和 LineItems。
這里有很多選項:JSON,XML,Java Serialization,Avro,Hadoop Writables 等。所有這些都是相同方法的變體:將對象圖編碼為字節數組。在對象模型發生變化的情況下,應該注意這種方法以確保向后兼容性,以便仍然可以從 HBase 中讀回較舊的持久性結構。
專業人員能夠以最少的 I / O 管理復雜的對象圖(例如,在這個例子中,單個 HBase Get per Order),但是缺點包括前面提到的關于序列化的向后兼容性,序列化的語言依賴性的警告(例如,Java 序列化)只適用于 Java 客戶端),事實上你必須反序列化整個對象以獲取 BLOB 中的任何信息,并且難以獲得像 Hive 這樣的框架來處理像這樣的自定義對象。
### 45.4。案例研究 - “高/寬/中”架構設計 Smackdown
本節將介紹出現在 dist 列表中的其他架構設計問題,特別是有關高表和寬表的問題。這些是一般準則,而不是法律 - 每個應用程序必須考慮自己的需求。
#### 45.4.1。行與版本
一個常見的問題是,是否應該選擇行或 HBase 的內置版本。上下文通常是要保留行的“很多”版本的地方(例如,它明顯高于 HBase 默認的 1 個最大版本)。行方法需要在 rowkey 的某些部分中存儲時間戳,以便它們不會在每次連續更新時覆蓋。
偏好:行(一般來說)。
#### 45.4.2。行與列
另一個常見問題是,是否應該更喜歡行或列。上下文通常在寬表的極端情況下,例如具有 1 行具有 100 萬個屬性,或者 1 百萬行,每行 1 列。
偏好:行(一般來說)。需要明確的是,本指南在非常廣泛的情況下,而不是在需要存儲幾十或一百列的標準用例中。但是這兩個選項之間也有一條中間路徑,那就是“Rows as Columns”。
#### 45.4.3。行作為列
行與列之間的中間路徑是打包數據,對于某些行,這些數據將成為列中的單獨行。 OpenTSDB 是這種情況的最佳示例,其中單行表示定義的時間范圍,然后將離散事件視為列。這種方法通常更復雜,可能需要重新編寫數據的額外復雜性,但具有 I / O 效率的優勢。有關此方法的概述,請參見 [schema.casestudies.log-steroids](#schema.casestudies.log_steroids) 。
### 45.5。案例研究 - 列出數據
以下是來自用戶 dist-list 的關于一個相當常見的問題的交換:如何處理 Apache HBase 中的每用戶列表數據。
* 問題 *****
我們正在研究如何在 HBase 中存儲大量(每用戶)列表數據,并且我們試圖找出哪種訪問模式最有意義。一種選擇是將大部分數據存儲在密鑰中,因此我們可以使用以下內容:
```
<FixedWidthUserName><FixedWidthValueId1>:"" (no value)
<FixedWidthUserName><FixedWidthValueId2>:"" (no value)
<FixedWidthUserName><FixedWidthValueId3>:"" (no value)
```
我們的另一個選擇是完全使用:
```
<FixedWidthUserName><FixedWidthPageNum0>:<FixedWidthLength><FixedIdNextPageNum><ValueId1><ValueId2><ValueId3>...
<FixedWidthUserName><FixedWidthPageNum1>:<FixedWidthLength><FixedIdNextPageNum><ValueId1><ValueId2><ValueId3>...
```
其中每行包含多個值。所以在一個案例中,讀取前 30 個值將是:
```
scan { STARTROW => 'FixedWidthUsername' LIMIT => 30}
```
在第二種情況下,它將是
```
get 'FixedWidthUserName\x00\x00\x00\x00'
```
一般使用模式是只讀取這些列表的前 30 個值,不經常訪問讀入更深入列表。有些用戶在這些列表中總共有 30 個值,有些用戶會有數百萬(即冪律分布)
單值格式似乎會占用 HBase 上更多的空間,但會提供一些改進的檢索/分頁靈活性。是否可以通過獲取與分頁掃描分頁來獲得顯著的性能優勢?
我最初的理解是,如果我們的分頁大小未知(并且適當地設置了緩存),那么掃描應該更快,但如果我們總是需要相同的頁面大小,則應該更快。我最后聽到不同的人告訴我有關表現的事情。我假設頁面大小相對一致,因此對于大多數用例,我們可以保證在固定頁面長度的情況下我們只需要一頁數據。我還假設我們不經常更新,但可能會插入這些列表的中間(意味著我們需要更新所有后續行)。
感謝您的幫助/建議/后續問題。
* 答案 *****
如果我理解正確,你最終會嘗試以“user,valueid,value”的形式存儲三元組,對吧?例如,類似于:
```
"user123, firstname, Paul",
"user234, lastname, Smith"
```
(但用戶名是固定寬度,valueids 是固定寬度)。
并且,您的訪問模式如下:“對于用戶 X,列出接下來的 30 個值,從 valueid Y 開始”。是對的嗎?這些值應該按 valueid 返回?
tl; dr 版本是你可能應該為每個用戶+值一行,而不是自己構建一個復雜的行內分頁方案,除非你真的確定它是需要的。
您的兩個選項反映了人們在設計 HBase 模式時遇到的常見問題:我應該“高”還是“寬”?您的第一個架構是“高”:每行代表一個用戶的一個值,因此每個用戶的表中有很多行;行鍵是 user + valueid,并且(可能)是單列限定符,表示“值”。如果你想按行排序按行排序掃描行(這是我上面的問題,關于這些 id 是否正確排序),這是很好的。您可以在任何用戶+ valueid 處開始掃描,閱讀下一個 30,然后完成。您放棄的是能夠為一個用戶的所有行提供事務保證,但聽起來并不像您需要的那樣。通常建議這樣做(參見 [https://hbase.apache.org/book.html#schema.smackdown](https://hbase.apache.org/book.html#schema.smackdown) )。
您的第二個選項是“寬”:您使用不同的限定符(其中限定符是 valueid)將一堆值存儲在一行中。這樣做的簡單方法是將一個用戶的所有值存儲在一行中。我猜你跳到了“分頁”版本,因為你假設在一行中存儲數百萬列對性能有害,這可能是也可能不是真的;只要你不是在單個請求中嘗試做太多,或者做一些事情,比如掃描并返回行中的所有單元格,它就不應該從根本上變得更糟。客戶端具有允許您獲取特定切片列的方法。
請注意,這兩種情況都不會從根本上占用更多的磁盤空間;您只是將標識信息的一部分“移動”到左側(進入行鍵,選項 1)或右側(進入選項 2 中的列限定符)。在封面下,每個鍵/值仍然存儲整個行鍵和列族名稱。 (如果這有點令人困惑,請花一個小時觀看 Lars George 關于理解 HBase 架構設計的精彩視頻: [http://www.youtube.com/watch?v=_HLoH_PgrLk](http://www.youtube.com/watch?v=_HLoH_PgrLk) )。
正如您所注意到的那樣,手動分頁版本具有更多復雜性,例如必須跟蹤每個頁面中有多少內容,如果插入新值則重新進行混洗等。這看起來要復雜得多。它可能在極高的吞吐量下具有一些輕微的速度優勢(或缺點!),并且真正了解它的唯一方法是嘗試它。如果你沒有時間來構建它并進行比較,我的建議是從最簡單的選項開始(每個用戶一行+值)。開始簡單并迭代! :)
## 46.運營和性能配置選項
### 46.1。調整 HBase 服務器 RPC 處理
* 將`hbase.regionserver.handler.count`(在`hbase-site.xml`中)設置為核心 x 主軸以實現并發。
* (可選)將呼叫隊列拆分為單獨的讀寫隊列,以實現差異化服務。參數`hbase.ipc.server.callqueue.handler.factor`指定呼叫隊列的數量:
* `0`表示單個共享隊列
* `1`表示每個處理程序的一個隊列。
* `0`和`1`之間的值分配隊列數與處理程序數成比例。例如,`.5`的值在每兩個處理程序之間共享一個隊列。
* 使用`hbase.ipc.server.callqueue.read.ratio`(0.98 中的`hbase.ipc.server.callqueue.read.share`)將呼叫隊列分成讀寫隊列:
* `0.5`表示將有相同數量的讀寫隊列
* `< 0.5`更多讀取而不是寫入
* `> 0.5`寫入比讀取更多
* 設置`hbase.ipc.server.callqueue.scan.ratio`(HBase 1.0+)將讀取呼叫隊列拆分為小讀取和長讀取隊列:
* 0.5 表示將有相同數量的短讀取和長讀取隊列
* `< 0.5`更多簡短閱讀
* `> 0.5`更長時間閱讀
### 46.2。禁用 Nagle for RPC
禁用 Nagle 的算法。延遲的 ACK 可以累加到 RPC 往返時間?200ms。設置以下參數:
* 在 Hadoop 的`core-site.xml`中:
* `ipc.server.tcpnodelay = true`
* `ipc.client.tcpnodelay = true`
* 在 HBase 的`hbase-site.xml`中:
* `hbase.ipc.client.tcpnodelay = true`
* `hbase.ipc.server.tcpnodelay = true`
### 46.3。限制服務器故障影響
盡可能快地檢測 regionserver 故障。設置以下參數:
* 在`hbase-site.xml`中,將`zookeeper.session.timeout`設置為 30 秒或更短時間以限制故障檢測(20-30 秒是一個良好的開始)。
* 檢測并避免不健康或失敗的 HDFS DataNodes:在`hdfs-site.xml`和`hbase-site.xml`中,設置以下參數:
* `dfs.namenode.avoid.read.stale.datanode = true`
* `dfs.namenode.avoid.write.stale.datanode = true`
### 46.4。在服務器端優化以實現低延遲
當 RegionServer 通過利用 HDFS 的[短路本地讀取](https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/ShortCircuitLocalReads.html)工具從 HDFS 讀取時,跳過網絡尋找本地塊。注意如何在連接的 datanode 和 dfsclient 端完成設置 - 即在 RegionServer 以及兩端如何加載 hadoop native `.so`庫。將 hadoop 設置 _dfs.client.read.shortcircuit_ 配置為 _true_ 并配置 datanode 的 _dfs.domain.socket.path_ 路徑并分享 dfsclient 后然后重新啟動,接下來配置 regionserver / dfsclient 端。
* 在`hbase-site.xml`中,設置以下參數:
* `dfs.client.read.shortcircuit = true`
* `dfs.client.read.shortcircuit.skip.checksum = true`所以我們不進行雙重校驗和(HBase 自己進行校驗和以節省 i / OS。有關詳細信息,請參閱 [`hbase.regionserver.checksum.verify`](#hbase.regionserver.checksum.verify.performance) 。
* `dfs.domain.socket.path`匹配為 datanodes 設置的內容。
* `dfs.client.read.shortcircuit.buffer.size = 131072`重要的是要避免 OOME - 如果未設置,hbase 有一個默認值,參見`hbase.dfs.client.read.shortcircuit.buffer.size`;它的默認值是 131072。
* 確保數據位置。在`hbase-site.xml`中,設置`hbase.hstore.min.locality.to.skip.major.compact = 0.7`(表示 0.7 <= n <= 1)
* 確保 DataNodes 具有足夠的塊傳輸處理程序。在`hdfs-site.xml`中,設置以下參數:
* `dfs.datanode.max.xcievers >= 8192`
* `dfs.datanode.handler.count =`錠數
重啟后檢查 RegionServer 日志。如果配置錯誤,您應該只會看到投訴。否則,短路讀取在后臺安靜地運行。它沒有提供指標,因此沒有關于其有效性的光學器件,但讀取延遲應顯示出顯著的改進,特別是如果良好的數據位置,大量隨機讀取和數據集大于可用緩存。
您可能會使用的其他高級配置,尤其是在日志中抱怨短路功能時,包括`dfs.client.read.shortcircuit.streams.cache.size`和`dfs.client.socketcache.capacity`。這些選項的文檔很少。你必須閱讀源代碼。
有關短路讀取的更多信息,請參閱 Colin 關于推出的舊博客,[如何改進短路本地讀取為 Hadoop 帶來更好的性能和安全性](http://blog.cloudera.com/blog/2013/08/how-improved-short-circuit-local-reads-bring-better-performance-and-security-to-hadoop/)。 [HDFS-347](https://issues.apache.org/jira/browse/HDFS-347) 問題也引發了一個有趣的讀物,顯示 HDFS 社區處于最佳狀態(請注意幾點評論)。
### 46.5。 JVM 調優
#### 46.5.1。調整 JVM GC 以獲得低收集延遲
* 使用 CMS 收集器:`-XX:+UseConcMarkSweepGC`
* 保持伊甸園空間盡可能小,以盡量減少平均收集時間。例:
```
-XX:CMSInitiatingOccupancyFraction=70
```
* 優化低收集延遲而不是吞吐量:`-Xmn512m`
* 并行收集伊甸園:`-XX:+UseParNewGC`
* 避免在壓力下收集:`-XX:+UseCMSInitiatingOccupancyOnly`
* 限制每個請求掃描儀結果大小,所以一切都適合幸存者空間,但沒有任期。在`hbase-site.xml`中,將`hbase.client.scanner.max.result.size`設置為伊甸園空間的 1/8(使用 - `Xmn512m`,這是~51MB)
* 設置`max.result.size` x `handler.count`小于幸存者空間
#### 46.5.2。操作系統級調整
* 關閉透明大頁面(THP):
```
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
```
* 設置`vm.swappiness = 0`
* 將`vm.min_free_kbytes`設置為至少 1GB(較大內存系統上為 8GB)
* 使用`vm.zone_reclaim_mode = 0`禁用 NUMA 區域回收
## 47.特殊情況
### 47.1。對于快速失敗比等待更好的應用
* 在客戶端的`hbase-site.xml`中,設置以下參數:
* 設置`hbase.client.pause = 1000`
* 設置`hbase.client.retries.number = 3`
* 如果你想騎過分裂和區??域移動,大幅增加`hbase.client.retries.number`(&gt; = 20)
* 設置 RecoverableZookeeper 重試次數:`zookeeper.recovery.retry = 1`(不重試)
* 在服務器端的`hbase-site.xml`中,設置 Zookeeper 會話超時以檢測服務器故障:`zookeeper.session.timeout`?30 秒(20-30 是好的)。
### 47.2。適用于可以容忍稍微過時信息的應用程序
**HBase 時間軸一致性(HBASE-10070)**啟用只讀副本后,區域(副本)的只讀副本將分布在群集上。一個 RegionServer 為默認或主副本提供服務,這是唯一可以為寫入提供服務的副本。其他 RegionServers 服務于輔助副本,遵循主 RegionServer,并且只能查看已提交的更新。輔助副本是只讀的,但可以在主服務器故障轉移時立即提供讀取,從而將讀取可用性從幾秒鐘縮短到幾毫秒。 Phoenix 支持時間軸一致性,自 4.4.0 提示:
* 部署 HBase 1.0.0 或更高版本。
* 在服務器端啟用時間軸一致的副本。
* 使用以下方法之一設置時間軸一致性:
* 使用`ALTER SESSION SET CONSISTENCY = 'TIMELINE’`
* 在 JDBC 連接字符串中將連接屬性`Consistency`設置為`timeline`
### 47.3。更多信息
有關操作和性能架構設計選項的更多信息,請參見性能部分 [perf.schema](#perf.schema) ,例如布隆過濾器,表配置的區域大小,壓縮和塊大小。
- HBase? 中文參考指南 3.0
- Preface
- Getting Started
- Apache HBase Configuration
- Upgrading
- The Apache HBase Shell
- Data Model
- HBase and Schema Design
- RegionServer Sizing Rules of Thumb
- HBase and MapReduce
- Securing Apache HBase
- Architecture
- In-memory Compaction
- Backup and Restore
- Synchronous Replication
- Apache HBase APIs
- Apache HBase External APIs
- Thrift API and Filter Language
- HBase and Spark
- Apache HBase Coprocessors
- Apache HBase Performance Tuning
- Troubleshooting and Debugging Apache HBase
- Apache HBase Case Studies
- Apache HBase Operational Management
- Building and Developing Apache HBase
- Unit Testing HBase Applications
- Protobuf in HBase
- Procedure Framework (Pv2): HBASE-12439
- AMv2 Description for Devs
- ZooKeeper
- Community
- Appendix