HBase客戶端代碼書寫規范

      網友投稿 664 2022-05-29

      本文檔提到的一些規范條例,主要來源于對定位問題過程中所積累的經驗、客戶端代碼實踐、對HBase Client源碼的分析以及已總結的ReleaseNotes中的一些注意點等。這些條款,主要分為規則、建議、示例三種類型。規則類,是在寫HBase客戶端代碼時必須遵循的一些條款。建議類,需要依據實際的應用需求來決定是否遵循。示例類,給出了一些功能代碼的實現示例,供參考,本文中使用的HBase版本仍為1.0+。

      1【規則】 Configuration實例的創建

      該類應該通過調用HBaseConfiguration的Create()方法來實例化。否則,將無法正確加載HBase中的相關配置項。

      正確示例:

      //該部分,應該是在類成員變量的聲明區域聲明

      private?Configuration?hbaseConfig?=?null;

      //最好在類的構造函數中,或者初始化方法中實例化該類

      hbaseConfig?= HBaseConfiguration.create();

      錯誤示例:

      hbaseConfig?= new Configuration();

      2【規則】 共享Configuration實例

      HBase客戶端代碼通過創建一個與Zookeeper之間的HConnection,來獲取與一個HBase集群進行交互的權限。一個Zookeeper的HConnection連接,對應著一個Configuration實例,已經創建的HConnection實例,會被緩存起來。也就是說,如果客戶端需要與HBase集群進行交互的時候,會傳遞一個Configuration實例過去,HBase Client部分通過已緩存的HConnection實例,來判斷屬于這個Configuration實例的HConnection實例是否存在,如果不存在,就會創建一個新的,如果存在,就會直接返回相應的實例。

      因此,如果頻頻的創建Configuration實例,會導致創建很多不必要的HConnection實例,很容易達到Zookeeper的連接數上限。

      建議在整個客戶端代碼范圍內,都共用同一個Configuration對象實例。

      3【規則】 HTable實例的創建

      HTable類有多種構造函數,如:

      1.public HTable(final String tableName)

      2.public HTable(final byte [] tableName)

      3.public HTable(Configuration conf, final byte [] tableName)

      4.public HTable(Configuration conf, final String tableName)

      5.public HTable(final byte[] tableName, final HConnection connection, final ExecutorService pool)

      建議采用第5種構造函數。之所以不建議使用前面的4種,是因為:前兩種方法實例化一個HTable時,沒有指定Configuration實例,那么,在實例化的時候,就會自動創建一個Configuration實例。如果需要實例化過多的HTable實例,那么,就可能會出現很多不必要的HConnection(關于這一點,前面部分已經有講述)。因此,而對于第3、4種構造方法,每個實例都可能會創建一個新的線程池,也可能會創建新的連接,導致性能偏低。

      正確示例:

      private HTable table = null;

      public initTable(Configuration config, byte[] tableName)

      {

      // sharedConn和pool都已經是事先實例化好的。建議進程級別內共享同一個。

      //?初始化HConnection的方法:

      // HConnection sharedConn =

      //??? HConnectionManager.createConnection(this.config);

      table = new HTable(config, tableName, sharedConn, pool);

      }

      錯誤示例:

      private HTable table = null;

      public initTable(String tableName)

      {

      table = new HTable(tableName);

      }

      public initTable(byte[] tableName)

      {

      table = new HTable(tableName);

      }

      4?【規則】 不允許多個線程在同一時間共用同一個HTable實例

      HTable是一個非線程安全類,因此,同一個HTable實例,不應該被多個線程同時使用,否則可能會帶來并發問題。

      5【規則】 HTable實例緩存

      如果一個HTable實例可能會被長時間且被同一個線程固定且頻繁的用到,例如,通過一個線程不斷的往一個表內寫入數據,那么這個HTable在實例化后,就需要緩存下來,而不是每一次插入操作,都要實例化一個HTable對象(盡管提倡實例緩存,但也不是在一個線程中一直沿用一個實例,個別場景下依然需要重構,可參見下一條規則)。

      正確示例:

      //注意該實例中提供的以Map形式緩存HTable實例的方法,未必通用。這與多線程多HTable實例的設計方案有關。如果確定一個HTable實例僅僅可能會被用于一個線程,而且該線程也僅有一個HTable實例的話,就無須使用Map。這里提供的思路僅供參考

      //該Map中以TableName為Key值,緩存所有已經實例化的HTable

      private Map demoTables = new HashMap();

      //所有的HTable實例,都將共享這個Configuration實例

      private Configuration demoConf = null;

      /**

      * <初始化一個HTable類>

      * <功能詳細描述>

      * @param tableName

      * @return

      * @throws IOException

      * @see [類、類#方法、類#成員]

      */

      private HTable initNewTable(String tableName) throws IOException

      {

      return new HTable(demoConf, tableName);

      }

      /**

      * <獲取HTable實例>

      * <功能詳細描述>

      * @see [類、類#方法、類#成員]

      */

      private HTable getTable(String tableName)

      {

      if (demoTables.containsKey(tableName))

      {

      return demoTables.get(tableName);

      } else {

      HTable table = null;

      try

      {

      table = initNewTable(tableName);

      demoTables.put(tableName, table);

      }

      catch (IOException e)

      {

      // TODO Auto-generated catch block

      e.printStackTrace();

      }

      return table;

      }

      }

      /**

      * <寫數據>

      * <這里未涉及到多線程多HTable實例在設計模式上的優化.這里只所以采用同步方法,

      *??是考慮到同一個HTable是非線程安全的.通常,我們建議一個HTable實例,在同一

      *??時間只能被用在一個寫數據的線程中>

      * @param dataList

      * @param tableName

      * @see [類、類#方法、類#成員]

      */

      public void putData(List dataList, String tableName)

      {

      HTable table = getTable(tableName);

      //關于這里的同步:如果在采用的設計方案中,不存在多線程共用同一個HTable實例

      //的可能的話,就無須同步了。這里需要注意的一點,就是HTable實例是非線程安全的

      synchronized (table)

      {

      try

      {

      table.put(dataList);

      table.notifyAll();

      }

      catch (IOException e)

      {

      //?在捕獲到IOE時,需要將緩存的實例重構。

      try {

      //?關閉之前的Connection.

      table.close();

      //?重新創建這個實例.

      table = new HTable(this.config, "jeason");

      } catch (IOException e1) {

      // TODO

      }

      }

      }

      }

      錯誤示例:

      public void putDataIncorrect(List dataList, String tableName)

      {

      HTable table = null;

      try

      {

      //每次寫數據,都創建一個HTable實例

      table = new HTable(demoConf, tableName);

      table.put(dataList);

      }

      catch (IOException e1)

      {

      // TODO Auto-generated catch block

      e1.printStackTrace();

      }

      finally

      {

      table.close();

      }

      }

      6【規則】 HTable實例寫數據的異常處理

      盡管在前一條規則中提到了提倡HTable實例的重構,但是,并非提倡一個線程自始至終要沿用同一個HTable實例,當捕獲到IOException時,依然需要重構HTable實例。示例代碼可參考上一個規則的示例。

      另外,勿輕易調用如下兩個方法:

      ?? Configuration#clear:

      這個方法,會清理掉所有的已經加載的屬性,那么,對于已經在使用這個Configuration的類或線程而言,可能會帶來潛在的問題(例如,假如HTable還在使用這個Configuration,那么,調用這個方法后,HTable中的這個Configuration的所有的參數,都被清理掉了),也就是說:只要還有對象或者線程在使用這個Configuration,我們就不應該調用這個clear方法,除非,所有的類或線程,都已經確定不用這個Configuration了。那么,這個操作,可以在所有的線程要退出的時候來做,而不是每一次。

      因此,這個方法,應該要放在進程要退出的地方去做。而不是每一次HBaseAdmin要重構的時候做。

      ?? HConnectionManager#deleteAllConnections:

      這個可能會導致現有的正在使用的連接被從連接集合中清理掉,同時,因為在HTable中保存了原有連接的引用,可能會導致這個連接無法關閉,進而可能會造成泄漏。因此,這個方法不建議使用。

      7【規則】 寫入失敗的數據要做相應的處理

      在寫數據的過程中,如果進程異常或一些其它的短暫的異常,可能會導致一些寫入操作失敗。因此,對于操作的數據,需要將其記錄下來。在集群恢復正常后,重新將其寫入到HBase數據表中。

      另外,有一點需要注意:HBase Client返回寫入失敗的數據,是不會自動重試的,僅僅會告訴接口調用者哪些數據寫入失敗了。對于寫入失敗的數據,一定要做一些安全的處理,例如可以考慮將這些失敗的數據,暫時寫在文件中,或者,直接緩存在內存中。

      正確示例:

      private List errorList = new ArrayList();

      /**

      * <采用PutList的模式插入數據>

      * <如果不是多線程調用該方法,可不采用同步>

      * @param put?一條數據記錄

      * @throws IOException

      * @see [類、類#方法、類#成員]

      */

      public synchronized void putData(Put put)

      {

      //?暫時將數據緩存在該List中

      dataList.add(put);

      //?當dataList的大小達到PUT_LIST_SIZE之后,就執行一次Put操作

      if (dataList.size() >= PUT_LIST_SIZE)

      HBase客戶端代碼書寫規范

      {

      try

      {

      demoTable.put(dataList);

      }

      catch (IOException e)

      {

      //?如果是RetriesExhaustedWithDetailsException類型的異常,

      //?說明這些數據中有部分是寫入失敗的這通常都是因為

      // HBase集群的進程異常引起,當然有時也會因為有大量

      //?的Region正在被轉移,導致嘗試一定的次數后失敗

      if (e instanceof RetriesExhaustedWithDetailsException)

      {

      RetriesExhaustedWithDetailsException ree =

      (RetriesExhaustedWithDetailsException)e;

      int failures = ree.getNumExceptions();

      for (int i = 0; i < failures; i++)

      {

      errorList.add(ree.getRow(i));

      }

      }

      }

      dataList.clear();

      }

      }

      8【規則】?資源釋放

      關于ResultScanner和HTable實例,在用完之后,需要調用它們的Close方法,將資源釋放掉。Close方法,要放在finally塊中,來確保一定會被調用到。

      正確示例:

      ResultScanner scanner = null;

      try

      {

      scanner = demoTable.getScanner(s);

      //Do Something here.

      }

      finally

      {

      scanner.close();

      }

      錯誤示例:

      1.?在代碼中未調用scanner.close()方法釋放相關資源。

      2. scanner.close()方法未放置在finally塊中:

      ResultScanner scanner = null;

      scanner = demoTable.getScanner(s);

      //Do Something here.

      scanner.close();

      9【規則】 Scan時的容錯處理

      Scan時不排除會遇到異常,例如,租約過期。在遇到異常時,建議Scan應該有重試的操作。

      事實上,重試在各類異常的容錯處理中,都是一種優秀的實踐,這一點,可以應用在各類與HBase操作相關的接口方法的容錯處理過程中。

      10【規則】 不用HBaseAdmin時,要及時關閉,HBaseAdmin實例不應常駐內存

      HBaseAdmin的示例應盡量遵循 “用時創建,用完關閉”的原則。不應該長時間緩存同一個HBaseAdmin實例。

      11【規則】 暫時不建議使用HTablePool獲取HTable實例,因為當前的HTablePool實現中可能會帶來泄露。創建HTable實例的方法,參考上面的規則4。

      12【建議】 不要調用HBaseAdmin的closeRegion方法關閉一個Region

      HBaseAdmin中,提供了關閉一個Region的接口:

      //hostAndPort可以指定,也可以不指定。

      public void closeRegion(final String regionname, final String hostAndPort)

      通過該方法關閉一個Region, HBase Client端會直接發RPC請求到Region所在的RegionServer上,整個流程對Master而言,是不感知的。也就是說,盡管RegionServer關閉了這個Region,但是,在Master側,還以為該Region是在該RegionServer上面打開的。假如,在執行Balance的時候,Master計算出恰好要轉移這個Region,那么,這個Region將無法被關閉,本次轉移操作將無法完成(關于這個問題,在當前的HBase版本中的處理的確還欠缺妥當)。

      因此,暫時不建議使用該方法關閉一個Region。

      13【建議】 采用PutList模式寫數據

      HTable類中提供了兩種寫數據的接口:

      1.???? public void put(final Put put) throws IOException

      2.???? public void put(final List puts) throws IOException

      第1種方法較之第2種方法,在性能上有明顯的弱勢。因此,寫數據時應該采用第2種方法。

      14【建議】 Scan時指定StartKey和EndKey

      一個有確切范圍的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"));? //?假設EndKey為rowB

      for(Result result : demoTable.getScanner(scan)) {

      // process Result instance

      }

      15【建議】 不要關閉WAL

      WAL是Write-Ahead-Log的簡稱,是指數據在入庫之前,首先會寫入到日志文件中,借此來確保數據的安全性。

      WAL功能默認是開啟的,但是,在Put類中提供了關閉WAL功能的接口:

      public void setWriteToWAL(boolean write)

      因此,不建議調用該方法將WAL關閉(即將writeToWAL設置為False),因為可能會造成最近1S(該值由RegionServer端的配置參數hbase.regionserver.optionallogflushinterval決定,默認為1S)內的數據丟失。但如果在實際應用中,對寫入的速率要求很高,并且可以容忍丟失最近1S內的數據的話,可以將該功能關閉。

      16【建議】 創建一張表或Scan時設定blockcache為 true

      HBase客戶端建表和scan時,設置blockcache=true。需要根據具體的應用需求來設定它的值,這取決于有些數據是否會被反復的查詢到,如果存在較多的重復記錄,將這個值設置為true可以提升效率,否則,建議關閉。

      建議按默認配置,默認就是true,只要不強制設置成false就可以,例如:

      HColumnDescriptor fieldADesc = new HColumnDescriptor("value".getBytes());

      fieldADesc.setBlockCacheEnabled(false);

      17【示例】 Configuration可以設置的參數

      為了能夠建立一個HBase Client端到HBase Server端的連接,需要設置如下幾個參數:

      hbase.zookeeper.quorum: Zookeeper的IP。多個Zookeeper節點的話,中間用”,”隔開。

      hbase.zookeeper.property.clientPort: Zookeeper的端口。

      說明:

      通過HBaseConfiguration.create()創建的Configuration實例,會自動加載如下配置文件中的配置項:

      1.???? core-default.xml

      2.???? core-site.xml

      3.???? hbase-default.xml

      4.???? hbase-site.xml

      因此,這四個配置文件,應該要放置在“Source Folder”下面(將一個文件夾設置為Source Folder的方法:如果在工程下面建立了一個resource的文件夾,那么,可以在該文件夾上右鍵鼠標,依次選擇”Build Path”->”Use as Source Folder”即可,可參考下圖)

      下面是客戶端可配置的一些參數集合(在通常情況下,這些值都不建議修改):

      參數名

      參數解釋

      hbase.client.pause

      每次異常或者其它情況下重試等待相關的時間參數(實際等待時間將根據該值與已重試次數計算得出)

      hbase.client.retries.number

      異常或者其它情況下的重試次數

      hbase.client.retries.longer.multiplier

      與重試次數有關

      hbase.client.rpc.maxattempts

      RPC請求不可達時的重試次數

      hbase.regionserver.lease.period

      與Scanner超時時間有關(單位ms)

      hbase.client.write.buffer

      在啟用AutoFlush的情況下,該值不起作用。如果未啟用AotoFlush的話,HBase Client端會首先緩存寫入的數據,達到設定的大小后才向HBase集群下發一次寫入操作

      hbase.client.scanner.caching

      Scan時一次next請求獲取的行數

      hbase.client.keyvalue.maxsize

      一條keyvalue數據的最大值

      hbase.htable.threads.max

      HTable實例中與數據操作有關的最大線程數

      hbase.client.prefetch.limit

      客戶端在寫數據或者讀取數據時,需要首先獲取對應的Region所在的地址。客戶端可以預緩存一些Region地址,這個參數就是與緩存的數目有關的配置

      正確設置參數的方法:

      hbaseConfig = HBaseConfiguration.create();

      //如下參數,如果在配置文件中已經存在,則無須再配置

      hbaseConfig.set("hbase.zookeeper.quorum", "157.5.100.1,157.5.100.2,157.5.100.3");

      hbaseConfig.set("hbase.zookeeper.property.clientPort", "2181");

      18【示例】 HTablePool在多線程寫入操作中的應用

      有多個寫數據線程時,可以采用HTablePool。現在先簡單介紹下該類的使用方法和注意點:

      (1)????????????? 多個寫數據的線程之間,應共享同一個HTablePool實例。

      (2)????????????? 實例化HTablePool的時候,應要指定最大的HTableInterface實例個數maxSize,即需要通過如下構造函數實例化該類:

      public HTablePool(final Configuration config, final int maxSize)

      關于maxSize的值,可以根據寫數據的線程數Threads以及涉及到的用戶表個數Tables來定,理論上,不應該超過(Threads*Tables)。

      (3)????????????? 客戶端線程通過HTablePool#getTable(tableName)的方法,獲取一個表名為tableName的HTableInterface實例。

      (4)????????????? 同一個HTableInterface實例,在同一個時刻只能給一個線程使用。

      (5)????????????? 如果HTableInterface使用用完了,需要調用HTablePool#putTable(HTableInterface table)方法將它放回去。

      示例代碼:

      /**

      *?寫數據失敗后需要一定的重試次數,每一次重試的等待時間,需要根據已經重試的次數而定.

      */

      private static final int[] RETRIES_WAITTIME = {1, 1, 1, 2, 2, 4, 4, 8, 16, 32};

      /**

      *?限定的重試次數

      */

      private static final int RETRIES = 10;

      /**

      *?失敗后等待的基本時間單位

      */

      private static final int PAUSE_UNIT = 1000;

      private static Configuration hadoopConfig;

      private static HTablePool tablePool;

      private static String[] tables;

      /**

      * <初始化HTablePool>

      * <功能詳細描述>

      * @param config

      * @see [類、類#方法、類#成員]

      */

      public static void initTablePool()

      {

      DemoConfig config = DemoConfig.getInstance();

      if (hadoopConfig == null)

      {

      hadoopConfig = HBaseConfiguration.create();

      hadoopConfig.set("hbase.zookeeper.quorum", config.getZookeepers());

      hadoopConfig.set("hbase.zookeeper.property.clientPort", config.getZookeeperPort());

      }

      if (tablePool == null)

      {

      tablePool = new HTablePool(hadoopConfig, config.getTablePoolMaxSize());

      tables = config.getTables().split(",");

      }

      }

      public void run()

      {

      //?初始化HTablePool.因為這是多線程間共享的一個實例,?僅被實例化一次.

      initTablePool();

      for (;;)

      {

      Map data = DataStorage.takeList();

      String tableName = tables[(Integer)data.get("table")];

      List list = (List)data.get("list");

      //?以Row為Key,保存List中所有的Put.該集合僅僅使用于寫入失敗時查找失敗的數據記錄.

      //?因為從Server端僅僅返回了失敗的數據記錄的Row值.

      Map rowPutMap = null;

      //?如果失敗了(哪怕是部分數據失敗),需要重試.每一次重試,都僅僅提交失敗的數據條目

      INNER_LOOP :

      for (int retry = 0; retry < RETRIES; retry++)

      {

      //?從HTablePool中獲取一個HTableInterface實例.用完后需要放回去.

      HTableInterface table = tablePool.getTable(tableName);

      try

      {

      table.put(list);

      //?如果執行到這里,說明成功了?.

      break INNER_LOOP;

      }

      catch (IOException e)

      {

      //?如果是RetriesExhaustedWithDetailsException類型的異常,

      //?說明這些數據中有部分是寫入失敗的這通常都是因為HBase集群

      //?的進程異常引起,當然有時也會因為有大量的Region正在被轉移,

      //?導致嘗試一定的次數后失敗.

      //?如果非RetriesExhaustedWithDetailsException異常,則需要將

      // list中的所有數據都要重新插入.

      if (e instanceof RetriesExhaustedWithDetailsException)

      {

      RetriesExhaustedWithDetailsException ree =

      (RetriesExhaustedWithDetailsException)e;

      int failures = ree.getNumExceptions();

      System.out.println("本次插入失敗了[" + failures + "]條數據.");

      //?第一次失敗且重試時,實例化該Map.

      if (rowPutMap == null)

      {

      rowPutMap = new HashMap(failures);

      for (int m = 0; m < list.size(); m++)

      {

      Put put = list.get(m);

      rowPutMap.put(put.getRow(), put);

      }

      }

      //先Clear掉原數據,然后將失敗的數據添加進來

      list.clear();

      for (int m = 0; m < failures; m++)

      {

      list.add(rowPutMap.get(ree.getRow(m)));

      }

      }

      }

      finally

      {

      //?用完之后,再將該實例放回去

      tablePool.putTable(table);

      }

      //?如果異常了,就暫時等待一段時間.該等待應該在將HTableInterface實例放回去之后

      try

      {

      sleep(getWaitTime(retry));

      }

      catch (InterruptedException e1)

      {

      System.out.println("Interruped");

      }

      }

      }

      }

      19【示例】 Put實例的創建

      HBase是一個面向列的數據庫,一行數據,可能對應多個列族,而一個列族又可以對應多個列。通常,寫入數據的時候,我們需要指定要寫入的列(含列族名稱和列名稱):

      如果要往HBase表中寫入一行數據,需要首先構建一個Put實例。Put中包含了數據的Key值和相應的Value值,Value值可以有多個(即可以有多列值)。

      有一點需要注意:在往Put實例中add一條KeyValue數據時,傳入的family,qualifier,value都是字節數組。在將一個字符串轉換為字節數組時,需要使用Bytes.toBytes方法,不要使用String.toBytes方法,因為后者無法保證編碼,尤其是在Key或Value中出現中文字符的時候,就會出現問題。

      代碼示例:

      //列族的名稱為privateInfo

      private final static byte[] FAMILY_PRIVATE = Bytes.toBytes("privateInfo");

      //列族privateInfo中總共有兩個列"name"&"address"

      private final static byte[] COLUMN_NAME = Bytes.toBytes("name");

      private final static byte[] COLUMN_ADDR = Bytes.toBytes("address");

      /**

      * <創建一個Put實例>

      * <在該方法中,將會創建一個具有1個列族,2列數據的Put>

      * @param rowKey? Key值

      * @param name????人名

      * @param address?地址

      * @return

      * @see [類、類#方法、類#成員]

      */

      public Put createPut(String rowKey, String name, String address)

      {

      Put put = new Put(Bytes.toBytes(rowKey));

      put.add(FAMILY_PRIVATE, COLUMN_NAME, Bytes.toBytes(name));

      put.add(FAMILY_PRIVATE, COLUMN_ADDR, Bytes.toBytes(address));

      return put;

      }

      20【示例】 HBaseAdmin實例的創建以及常用方法

      代碼示例:

      private Configuration demoConf = null;

      private HBaseAdmin hbaseAdmin = null;

      /**

      * <構造函數>

      *?需要將已經實例化好的Configuration實例傳遞進來

      */

      public HBaseAdminDemo(Configuration conf)

      {

      this.demoConf = conf;

      try

      {

      //?實例化HBaseAdmin

      hbaseAdmin = new HBaseAdmin(this.demoConf);

      }

      catch (MasterNotRunningException e)

      {

      e.printStackTrace();

      }

      catch (ZooKeeperConnectionException e)

      {

      e.printStackTrace();

      }

      }

      /**

      * <一些方法使用示例>

      * <更多的方法,請參考HBase接口文檔>

      * @throws IOException

      * @throws ZooKeeperConnectionException

      * @throws MasterNotRunningException

      * @see [類、類#方法、類#成員]

      */

      public void demo() throws MasterNotRunningException, ZooKeeperConnectionException, IOException

      {

      byte[] regionName = Bytes.toBytes("mrtest,jjj,1315449869513.fc41d70b84e9f6e91f9f01affdb06703.");

      byte[] encodeName = Bytes.toBytes("fc41d70b84e9f6e91f9f01affdb06703");

      //?重新分配一個Reigon.

      hbaseAdmin.unassign(regionName, false);

      //?主動觸發Balance.

      hbaseAdmin.balancer();

      //?移動一個Region,第2個參數,是RegionServer的HostName+StartCode,例如:

      // host187.example.com,60020,1289493121758.如果將該參數設置為null,則會隨機移動該Region

      hbaseAdmin.move(encodeName, null);

      //?判斷一個表是否存在

      hbaseAdmin.tableExists("tableName");

      //?判斷一個表是否被激活

      hbaseAdmin.isTableEnabled("tableName");

      }

      /**

      * <快速創建一個表的方法>

      * <首先創建一個HTableDescriptor實例,它里面包含了即將要創建的HTable的描述信息,同時,需要創建相應的列族。列族關聯的實例是HColumnDescriptor。在本示例中,創建的列族名稱為“columnName”>

      * @param tableName?表名

      * @return

      * @see [類、類#方法、類#成員]

      */

      public boolean createTable(String tableName)

      {

      try {

      if (hbaseAdmin.tableExists(tableName)) {

      return false;

      }

      HTableDescriptor tableDesc = new HTableDescriptor(tableName);

      HColumnDescriptor fieldADesc = new HColumnDescriptor("columnName".getBytes());

      fieldADesc.setBlocksize(640 * 1024);

      tableDesc.addFamily(fieldADesc);

      hbaseAdmin.createTable(tableDesc);

      } catch (Exception e) {

      e.printStackTrace();

      return false;

      }

      return true;

      }

      附 1 ?Scan時的兩個關鍵參數—Batch和Caching

      Batch:使用scan調用next接口每次最大返回的記錄數,與一次讀取的列數有關。

      Caching:一個RPC查詢請求最大的返回的next數目,與一次RPC獲取的行數有關。

      首先舉幾個例子,來介紹這兩個參數在Scan時所起到的作用:

      假設表A的一個Region中存在2行(rowkey)數據,每行有1000column,且每列當前只有一個version,即每行就會有1000個key value。

      ColuA1

      ColuA2

      ColuA3

      ColuA4

      ………

      ColuN1

      ColuN2

      ColuN3

      ColuN4

      Row1

      ………

      Row2

      ………

      例1: 查詢參數: 不設batch,設定caching=2

      那么,一次RPC請求,就會返回2000個KeyValue.

      例2: 查詢參數: 設定batch=500,設定caching=2

      那么,一次RPC請求,只能返回1000個KeyValue.

      例3: 查詢參數: 設定batch=300,設定caching=4

      那么,一次RPC請求,也只能返回1000個KeyValue.

      關于Batch和Caching的進一步解釋:

      ?? 一次Caching,是一次請求數據的機會。

      ?? 同一行數據是否可以通過一次Caching讀完,取決于Batch的設置,如果Batch的值小于一行的總列數,那么,這一行至少需要2次Caching才可以讀完(后面的一次Caching的機會,會繼續前面讀取到的位置繼續讀取)。

      ?? 一次Caching讀取,不能跨行。如果某一行已經讀完,并且Batch的值還沒有達到設定的大小,也不會繼續讀下一行了。

      那么,關于例1與例2的結果,就很好解釋了:

      例1的解釋:

      不設定Batch的時候,默認會讀完改行所有的列。那么,在caching為2的時候,一次RPC請求就會返回2000個KeyValue。

      例2的解釋:

      設定Batch為500,caching為2的情況下,也就是說,每一次Caching,最多讀取500列數據。那么,第一次Caching,讀取到500列,剩余的500列,會在第2次Caching中讀取到。因此,兩次Caching會返回1000個KeyValue。

      例3的解釋:

      設定Batch為300,caching為4的情況下,讀取完1000條數據,正好需要4次caching。因此,只能返回1000條數據。

      代碼示例:

      Scan s = new Scan();

      //設置查詢的起始key和結束key

      s.setStartRow(Bytes.toBytes("01001686138100001"));

      s.setStopRow(Bytes.toBytes("01001686138100002"));

      s.setBatch(1000);

      s.setCaching(100);

      ResultScanner scanner = null;

      try {

      scanner = tb.getScanner(s);

      for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {

      for (KeyValue kv : rr.raw()) {

      //顯示查詢的結果

      System.out.println("key:" + Bytes.toString(kv.getRow())

      + "getQualifier:" + Bytes.toString(kv.getQualifier())

      + "value" + Bytes.toString(kv.getValue()));

      }

      }

      } catch (IOException e) {

      System.out.println("error!" + e.toString());

      } finally {

      scanner.close();

      }

      EI企業智能

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:OpenHarmony設備的開發環境搭建與工程管理(Ubuntu系統環境)
      下一篇:Unity 接入友盟統計過程詳解(IOS)
      相關文章
      国产成人精品日本亚洲专区| 亚洲第一精品电影网| 亚洲成av人片不卡无码久久 | 亚洲国产成人精品女人久久久| 亚洲人成电影网站免费| 亚洲制服丝袜在线播放| 久久久久亚洲精品天堂久久久久久| 国产成人精品亚洲| 亚洲毛片av日韩av无码| 亚洲精华液一二三产区| 精品国产亚洲第一区二区三区| 国产精品亚洲精品日韩动图 | 亚洲嫩模在线观看| 久久99亚洲网美利坚合众国| 亚洲一区二区三区日本久久九| 久久久久久亚洲精品| 久久久婷婷五月亚洲97号色| 亚洲欧洲在线播放| 亚洲a∨无码男人的天堂| 久久久久久亚洲精品影院| 久久久久久亚洲精品影院| 亚洲高清毛片一区二区| 国产AV无码专区亚洲AV琪琪| 亚洲国产精品碰碰| 国产亚洲色婷婷久久99精品91| 亚洲精品乱码久久久久久中文字幕 | 亚洲深深色噜噜狠狠爱网站| 亚洲第一AV网站| 亚洲天天做日日做天天看| 亚洲国产成人久久综合一区| 国产成人精品日本亚洲18图| 亚洲一卡2卡三卡4卡无卡下载| 精品久久久久久久久亚洲偷窥女厕| 亚洲国产精品无码久久九九| 亚洲人成色77777| 亚洲视频小说图片| 亚洲砖码砖专无区2023| 亚洲AV永久无码精品一区二区国产| 国产L精品国产亚洲区久久| 久久精品国产亚洲AV网站| 亚洲综合区图片小说区|