
# HBase应用开发建议
#### 不建议调用Admin的closeRegion方法关闭一个Region
Admin中，提供了关闭一个Region的接口：
**public void closeRegion(final String regionname, final String serverName)**
通过该方法关闭一个Region，HBase Client端会直接发RPC请求到Region所在的RegionServer上，整个流程对Master而言，是不感知的。也就是说，尽管RegionServer关闭了这个Region，但是，在Master侧，还以为该Region是在该RegionServer上面打开的。假如，在执行Balance的时候，Master计算出恰好要转移这个Region，那么，这个Region将无法被关闭，本次转移操作将无法完成（关于这个问题，在当前的HBase版本中的处理的确还欠缺妥当）。
因此，暂时不建议使用该方法关闭一个Region。
#### 采用PutList模式写数据
Table类中提供了两种写数据的接口：
1. public void put(final Put put) throws IOException
2. public void put(final List\<Put\> puts) throws IOException
第1种方法较之第2种方法，在性能上有明显的弱势。因此，写数据时应该采用第2种方法。
#### Scan时指定StartKey和StopKey
一个有确切范围的Scan，在性能上会带来较大的好处。
**代码示例：**
```
Scan scan = new Scan();
scan.addColumn(Bytes.toBytes("familyname"),Bytes.toBytes("columnname"));
scan.setStartRow( Bytes.toBytes("rowA")); // 假设起始Key为rowA
scan.setStopRow( Bytes.toBytes("rowB")); // 假设结束Key为rowB
for(Result result : demoTable.getScanner(scan)) {
// process Result instance
}
```
#### 不要关闭WAL
WAL是Write-Ahead-Log的简称，是指数据在入库之前，首先会写入到日志文件中，借此来确保数据的安全性。
WAL功能默认是开启的，但是，在Put类中提供了关闭WAL功能的接口：
public void setWriteToWAL(boolean write)
因此，不建议调用该方法将WAL关闭（即将writeToWAL设置为False），因为可能会造成最近1秒（该值由RegionServer端的配置参数"hbase.regionserver.optionallogflushinterval"决定）内的数据丢失。但如果在实际应用中，对写入的速率要求很高，并且可以容忍丢失最近1秒内的数据的话，可以将该功能关闭。
#### 创建一张表或Scan时设定blockcache为true
HBase客户端建表和scan时，设置blockcache=true。需要根据具体的应用需求来设定它的值，这取决于有些数据是否会被反复的查询到，如果存在较多的重复记录，将这个值设置为true可以提升效率，否则，建议关闭。
建议按默认配置，默认就是true，只要不强制设置成false就可以，例如：
```
HColumnDescriptor fieldADesc = new HColumnDescriptor("value".getBytes());
fieldADesc.setBlockCacheEnabled(false);
```
#### HBase不支持条件查询和Order By等查询方法，存储按照字典排序，读取只支持RowKey扫描
设计时应避免HBase随机查找、排序的应用场景。
#### 业务表设计建议
1. 预分Region，使Region分布均匀，提高并发
2. 避免过多的热点Region。根据应用场景，可考虑将时间因素引入RowKey。
3. 同时访问的数据尽量连续存储。同时读取的数据相邻存储；同时读取的数据存放在同一行；同时读取的数据存放在同一cell。
4. 查询频繁属性放在RowKey前面部分。RowKey的设计在排序上必须与主要的查询条件契合。
5. 离散度较好的属性作为RowKey组成部分。分析数据离散度特点以及查询场景，综合各种场景进行设计。
6. 存储冗余信息，提高检索性能。使用二级索引，适应更多查询场景。
7. 利用过期时间、版本个数设置等操作，让表能自动清除过期数据。
![](https://support.huaweicloud.com/devg-rule-mrs/public_sys-resources/note_3.0-zh-cn.png)
在HBase中，一直在繁忙写数据的Region被称为热点Region。
#### HBase客户端超时配置建议
HBase客户端相关超时配置参数如下：
- hbase.rpc.timeout：表示RPC调用的超时时间，单位为毫秒，默认值为60000（60秒），用于控制单次RPC调用的最大耗时。
- hbase.client.scanner.timeout.period：Scanner定义扫描操作的超时时间，单位为毫秒，默认值为360000（360秒）。如果在此时间内没有数据返回，则认为超时。
- hbase.client.operation.timeout：客户端操作超时时间，默认值为120000毫秒（2分钟），包括一次完整的客户端操作（例如Get、Put、Delete等）。
- hbase.client.retries.number：最大重试次数，用于表示所有可重试操作所支持的最大重试次数。
客户端配置超时参数值时需要考虑以下条件，保证客户端配置的总超时时间可覆盖重试操作：
hbase.client.operation.timeout \> hbase.rpc.timeout \* hbase.client.retries.number
业务对请求时延有要求时，可适当调整上述配置，建议"hbase.rpc.timeout" 值不小于5秒，重试次数不少于5次，以保证业务的稳定性。
#### HBase建表预分区
预分区与不预分区HBase数据表之间的区别
| 维度           | 不预分区                         | 预分区                    |
|:---|:---|:---|
| 初始状态         | 建表后只有1个Region，所有写入操作集中到一台机器。 | 建表即有多个Region，写入操作均匀分布。 |
| Region Split | 数据增长触发自动Split，引发数据迁移和I/O风暴。  | 避免自动Split带来的性能抖动。      |
| 冷启动性能        | 初始阶段写入性能极差。                  | 初始阶段就具备水平扩展能力。         |
| 运维复杂度        | 需要频繁处理热点和Split告警。            | Region分布稳定，运维成本低。      |
   
**预分区核心概念**
```
预分区边界
 Region 1        Region 2        Region 3
[  , "1a")      ["1a", "3c")    ["3c",   )
   ↓               ↓               ↓
RegionServer A  RegionServer B  RegionServer C
```
- 分区边界（Split Key）：定义每个Region的RowKey范围。
- Region数量：由分区边界决定，边界数 = Region数量 - 1。
- 数据路由：RowKey根据字典序落入对应Region。
**预分区策略**
- **基于HexStringSplit预分区**
  **适用场景**：适用于RowKey前缀为hex散列值（0\~9, a\~f）的场景。
  **建表语句**：
  方式一：手动指定16进制分区分界点
  ```
  create 'my_table', 'cf', SPLITS => [
    '1', '2', '3', '4', '5', '6', '7', '8', '9',
    'a', 'b', 'c', 'd', 'e', 'f'
  ]
  ```
  方式二：使用HBase Shell内置分割器
  ```
  create 'my_table', 'cf', {NUMREGIONS => 16, SPLITALGO => 'HexStringSplit'}
  ```
  **分区分布**：
  
  | Region    | RowKey范围 |
  |:---|:---|
  | Region 1  | \[ , 1)  |
  | Region 2  | \[1, 2)  |
  | ...       | ...      |
  | Region 16 | \[f, )   |
     
  
- **基于业务数据量估算预分区**
  **适用场景**：适用于数据量可预估，需要更精细分区控制的场景。
  **计算步骤**：
  1. 预估总数据量（行数和存储量）。
  
  2. 确定单Region目标大小。
  
  3. 计算预分区数量。 预分区计算公式：Region数量 = 预估总数据量（GB） / 单Region目标大小（GB）。
     在预分区时需要考虑未来数据增长：
     - 预分区数量按1.5\~2倍当前数据量计算。
     
     - 初始Region可适当偏小（5\~7GB），为数据增长留出空间。
     
     - 设置合理的"MAX_FILESIZE"或集群级别设置合理的Region分裂大小，控制自动Split阈值。
     
     - 可以根据业务所需合理地调整split策略。
     
     
     Region分裂相关配置**：**
     
     | 参数名                                    | 参数说明                                                                                                                                            | 默认值                                                                |
     |:---|:---|:---|
     | hbase.hregion.max.filesize             | HStoreFile的最大大小（单位：字节）。如果任何一个列族的HStoreFile超过此参数值，托管的HRegion将会被拆分为两个。                                                                            | 10G                                                                |
     | hbase.regionserver.region.split.policy | 定义split策略。目前可用的split策略有ConstantSizeRegionSplitPolicy、DisabledRegionSplitPolicy、DelimitedKeyPrefixRegionSplitPolicy、KeyPrefixRegionSplitPolicy等。 | org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy |
        
     
  
  4. 根据散列前缀范围生成分区边界。
   
- **基于时间范围预分区**
  **适用场景**：适用于RowKey包含时间前缀，且查询模式以时间范围为主的场景。
  时间前缀容易产生写热点（当前时间的数据集中写入），所以仅在数据写入量可控或已通过其他方式打散的场景使用。
  **按月分区注意事项**：
  - 不同月份的数据量可能差异较大，导致Region大小不均。
  
  - 需定期评估是否需要手动Split大Region或Merge小Region。
  
  
  **预分区策略选择**
  
  | 场景             | 推荐策略           | 原因                    |
  |:---|:---|:---|
  | RowKey前缀为hex散列 | HexStringSplit | 天然对齐16进制前缀，分区均匀。      |
  | 数据量可预估         | 业务数据量估算        | 精确控制Region大小，避免过小或过大。 |
  | 按时间查询为主        | 时间范围预分区        | Scan效率高，但需注意写热点。      |
  | 数据分布不可预知       | UniformSplit   | 字节空间均匀切分，通用性强。        |
     
  
 
