HBase學習筆記 (叄)- HBase進階


什麼導致hbase性能下降?

  • jvm內存分配與gc回收策略
  • 與hbase運行機制相關的部分配置不合理
  • 表結構設計及用戶使用方式不合理

Hbase概念

Hbase數據存儲過程·

  • Hbase寫入時當memstore達到一定的大小會flush到磁盤保存成 HFile, 當hfile小文件太多會執行compact操作進行合併。(當一個hstore裏只包含一個hfile時;查詢效率纔是最大化。因爲hfile小文件過多會影響查詢時長,合併後纔可以提高查詢效率。)
  • 當region的大小達到某一閾值之後, 會執行split操作

compaction分兩種:

  • Minor compaction: 選取一些小的 、 相鄰的storefile將他們合併成一個更大的storefile
  • Major compaction: 將所有的storefile合併成一個storefile, 清理無意義數據、被刪除的數據、ttl過期數據 、 版本號超過設定版本號的數據;資源消耗最大。
  • Split: 當一個region達到一定的大小就會自動split成兩個region

compact促發條件:

  • Memstore被flush到磁盤,
  • 用戶執行shell命令compact, Major_compact 或者調用了相應的api
  • Hbase後臺線程週期性觸發檢查

Hbase優化策略

hbase優化

  • 常見服務端配置優化
  • 常用優化策略 (以實際需求爲主)
  • Hbase讀/寫性能優化

HBase優化策略一:服務端優化策略

  • Jvm設置與gc設置
  • hbase-site. xml 部分屬性配置
    max.filesize默認10G;
    majorcompaction默認1天建議0,通過手動合併,因爲需要用到很長時間;
    min默認3;
    在這裏插入圖片描述
    cache.size在偏向讀的業務當中可以適當調大一些。
    flush.size可以設置大一點;
    block.multiplier建議設置成5;如果太大會有內存溢出的危險。
    在這裏插入圖片描述
    (hbase.hstore.blockingStoreFiles:默認爲7,如果任何一個store(非.META.表裏的store)的storefile的文件數大於該值,則在flush memstore前先進行split或者compact,同時把該region添加到flushQueue,延時刷新,這期間會阻塞寫操作直到compact完成或者超過hbase.hstore.blockingWaitTime(默認90s)配置的時間,可以設置爲30,避免memstore不及時flush。當regionserver運行日誌中出現大量的“Region <regionName> has too many store files; delaying flush up to 90000ms"時,說明這個值需要調整了)

HBase優化策略二:常用優化策略

HBase常優用化
-預先分區 -RowKey優化 -Column優化 -Schema優化

  • 預先分區
    • 創建hbase表的時候會自動創建一個region分區(hbase默認建一個regionserver上建region;後期數據大會分爲兩個region)
    • 創建hbase表的時候預先創建一些空的regions(減少io操作;通過預先分區;解決數據傾斜問題;將頻繁訪問的數據放到多個region中;將不常訪問的分區放到一個或幾個region中)
  • Rowkey優化
    • 利用hbase默認排序特點, 將一起訪問的數據放到一起
    • 防止熱點問題(大量的client集中訪問一個節點), 避免使用時序或者單調的遞增遞減等
  • Column優化
    • 列族的名稱和列的描述命名儘量簡短(過長會佔據內存空間)
    • 同一張表中columnfamily的數量不要超過 3 個
  • Schema優化
    • 寬表: 一種 “列多行少” 的設計(事物性能好;hbase的事物建立在行的基礎上的;行少插入的時候可以有很好的保障)
    • 高表: 一種 “列少行多” 的設計(查詢性能好;查詢的條件放入rowkey中我們可以緩存更多的行;元數據來講開銷大因爲行多rowkey多)
      hbase主要在於不必苛刻設計於哪種;主要取決於業務。

HBase優化策略三:讀寫優化策略

  • Hbase寫優化策略
    • 同步批量提交or異步批量提交
    • WAL優化, 是否必須, 持久化等級

默認時同步提交數據的;異步提交是可能丟失一些數據的;在業允許的情況下可以開啓。

WAL默認是開啓的;一方面確保數據緩存丟失了也可以數據恢復;另一方面是爲了集羣間的異步複製;更關注寫入的吞吐量的時候可以關閉WAL或者採用異步寫入。

  • Hbase讀優化策略
    • 客戶端: Scan緩存設置, 批量獲取
    • 服務端: blockcache配置是否合理, Hfile是否過多
    • 表結構的設計問題

在設置scan檢索的時候可以設置scan的cache;scan在檢索的時候不會一次將數據加載到本地;而是多次rpc請求加載(防止影響其他業務或者數據量大造成內存溢出);cache默認100條大小設置的大一點可以減少rpc的請求數據次數。

查詢數據的時候blockcache如果不能命中;還要去Hfile里拉取數據。
Hfile不能過多;過多會導致查詢緩慢;需要compact合併。

HBase協處理器簡介

  • HBase coprocessor
    • Hbase協處理器受bigtable協處理器的啓發, 爲用戶提供類庫和運行時環境, 使得代碼能夠在hbase regionserver和master上處理
    • 系統協處理器and表協處理器(coprocessor分爲兩類協處理器)
    • Observer and Endpoint(HBase 提供的coprocessor插件)
    • 系統協處理器: 全局加載到regionserver託管的所有表和region上(針對整個集羣)
    • 表協處理器: 用戶可以指定一張表使用協處理器(針對單張表)
    • 觀察者 (Observer): 類似於關係數據庫的觸發器
    • 終端 (Endpoint): 動態的終端有點像存儲過程
  • Observer
    • Regionobserver: 提供客戶端的數據操縱事件鉤子: Get, Put,Delete, Scan等
    • Masterobserver: 提供DDL類型的操作鉤子。如創建、刪除修改數據表等
    • Walobserver: 提供wal相關操作鉤子
  • Observer應用場景
    • 安全性: 例如執行get或put操作前, 通過preget或preput方法檢查, 是否允許該操作
    • 引用完整性約束: hbase並不支持關係型數據庫中的引用完整性約束概念, 即通常所說的外鍵;我們可以使用協處理器增強這種約束
  • Endpoint
    • endpoint是動態rpc插件的接口, 它的實現代碼被安裝在服務器端, 從而能夠通過hbase Rpc喚醒
    • 調用接口, 它們的實現代碼會被目標regionserver遠程執行·
    • 典型的案例: 一個大table有幾百個region, 需要計算某列的平均值或者總和

HBase實戰:開發RegionObserver協處理器

◆實現一個endpoint類型的協處理器

  • 待整理

◆實現一個regionobserver類型的協處理器

package com.kun.hbase;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.CoprocessorEnvironment;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Durability;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
import org.apache.hadoop.hbase.util.Bytes;



/**
 * Created by jixin on 18-2-25.
 */
public class RegionObserverTest extends BaseRegionObserver {

  private byte[] columnFamily = Bytes.toBytes("cf");
  private byte[] countCol = Bytes.toBytes("countCol");
  private byte[] unDeleteCol = Bytes.toBytes("unDeleteCol");
  private RegionCoprocessorEnvironment environment;

  //regionserver 打開region前執行
  @Override
  public void start(CoprocessorEnvironment e) throws IOException {
    environment = (RegionCoprocessorEnvironment) e;
  }

  //RegionServer關閉region前調用
  @Override
  public void stop(CoprocessorEnvironment e) throws IOException {

  }

  /**
   * 需求一
   * 1. cf:countCol 進行累加操作。 每次插入的時候都要與之前的值進行相加
   * 需要重載prePut方法
   */

  @Override
  public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit,
      Durability durability) throws IOException {
    if (put.has(columnFamily, countCol)) {//獲取old countcol value
      Result rs = e.getEnvironment().getRegion().get(new Get(put.getRow()));
      int oldNum = 0;
      for (Cell cell : rs.rawCells()) {
        if (CellUtil.matchingColumn(cell, columnFamily, countCol)) {
          oldNum = Integer.valueOf(Bytes.toString(CellUtil.cloneValue(cell)));
        }
      }

      //獲取new countcol value
      List<Cell> cells = put.get(columnFamily, countCol);
      int newNum = 0;
      for (Cell cell : cells) {
        if (CellUtil.matchingColumn(cell, columnFamily, countCol)) {
          newNum = Integer.valueOf(Bytes.toString(CellUtil.cloneValue(cell)));
        }
      }

      //sum AND update Put實例
      put.addColumn(columnFamily, countCol, Bytes.toBytes(String.valueOf(oldNum + newNum)));
    }
  }

  /**
   * 需求二
   * 2. 不能直接刪除unDeleteCol    刪除countCol的時候將unDeleteCol一同刪除
   * 需要重載preDelete方法
   */
  @Override
  public void preDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete delete,
      WALEdit edit,
      Durability durability) throws IOException {
    //判斷是否操作刪除了cf列族
    List<Cell> cells = delete.getFamilyCellMap().get(columnFamily);
    if (cells == null || cells.size() == 0) {
      return;
    }

    boolean deleteFlag = false;
    for (Cell cell : cells) {
      byte[] qualifier = CellUtil.cloneQualifier(cell);

      if (Arrays.equals(qualifier, unDeleteCol)) {
        throw new IOException("can not delete unDel column");
      }

      if (Arrays.equals(qualifier, countCol)) {
        deleteFlag = true;
      }
    }

    if (deleteFlag) {
      delete.addColumn(columnFamily, unDeleteCol);
    }
  }

}

HBase實戰:HBase協處理器加載

  • 待整理

  • 配置文件加載: 即通過hbase-site. Xml文件配置加載, 一般這樣的協處理器是系統級別的.
    在這裏插入圖片描述
    在這裏插入圖片描述

  • Shell加載: 可以通過alter命令來對錶進行schema修改來加載協處理器

  • shell加載;jar包|全類名|優先級(會自動分配)|協處理器相關參數
    在這裏插入圖片描述
    首先對regionobserver進行編譯 mvn clean install
    上傳到hdfs集羣中
    測試協處理器
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述
    在這裏插入圖片描述

  • 通過api代碼加載: 即通過api的方式來加載協處理器
    配置文件加載

Hbase卸載/更新協處理器

  • 重複加載的第二個coprocessor實例不會發揮作用
  • 要完成卸載/更新就需要重啓JVM, 也就是重啓regionserver
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章