HBASE之JAVA API學習筆記

本文是對如何使用Hbase提供的Java Api編寫Hbase客戶端程序的一個學習總結,共包括如下章節(jié)的內容:

  • 概述
  • 更新和查詢數(shù)據(jù)
  • 創(chuàng)建和更新表結構
  • 編譯和運行
  • 遠程訪問Hbase

參考資料:

1、如果需要了解Hbase的基本知識,可參見《HBASE學習筆記》一文。

一、概述

Hbase提供了一套Java API,可以讓編寫java程序來訪問Hbase。從HBase 1.0開始,Hbase提供了新的Api替代以前的老的Api,新的Api更加干凈和直觀。
新的Api使用接口,而不是特定的類來操作API。新的Api,使用ConnectionFactory的工廠方法來獲取一個Connection對象,然后調用getAdmin()和getTable()方法來獲取Admin和Table實例,接著使用實例完成具體操作,最后關閉實例和連接。
本文用的hbase版本是hbase2.0.2,將使用新的API進行介紹。新舊API一個非常直觀的區(qū)別是,帶H開頭的是舊的API,如Htable,HBaseAdmin,HTableDescriptor,HColumnDescriptor等。
java客戶端是通過zookeeper找到hbase服務的,因此,需要在代碼中配置zookeeper的信息。

二、更新和查詢數(shù)據(jù)

我們通過具體例子來說明,假設hbase中已經(jīng)有一張表test,表中有一個列族data。關鍵的API是Table接口。

(一)插入數(shù)據(jù)

本例子代碼是往表中插入數(shù)據(jù),完整的代碼如下:

package com.hbaseexample;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.util.Bytes;
public class Example {
    private Connection connection = null;
    private Table table = null;
    public static void main(String[] args) {
        Example example = new Example();
        try {
            example.conn();
            example.addData();
            System.out.println("finish");
        } catch (IOException e) {e.printStackTrace();
        } finally {example.close();}
    }
    public void conn() throws IOException {
        Configuration config = HBaseConfiguration.create();
        config.set("hbase.zookeeper.quorum", "127.0.0.1");//zk地址
        config.set("hbase.zookeeper.property.clientPort", "2181");//zk端口
        connection = ConnectionFactory.createConnection(config);
        table = connection.getTable(TableName.valueOf("test"));
    }
    public void close() {
        if (table != null)
            try {table.close();} catch (IOException e) {}
        if (connection != null)
            try {connection.close();    } catch (IOException e) {}
    }
    public void addData() throws IOException {
        Put p = new Put(Bytes.toBytes("rk_100001")); //行鍵 
        //addColumn第1個參數(shù)是列族,第2個參數(shù)是列名,第3個參數(shù)是插入的值
        p.addColumn(Bytes.toBytes("data"), Bytes.toBytes("uname"), Bytes.toBytes("tom"));
        p.addColumn(Bytes.toBytes("data"), Bytes.toBytes("age"), Bytes.toBytes("25"));
        table.put(p);
    }
}

上面代碼是一個完整的帶main方法的java類,首先獲得一個Connection對象,然后獲取到Table對象,再創(chuàng)建一個Put對象保存要插入的數(shù)據(jù),最后利用Table對象提交數(shù)據(jù)。

下面例子代碼都是在這個代碼基礎上運行。

(二)按鍵值查詢數(shù)據(jù)

如果我們要查詢指定的數(shù)據(jù),可以利用Get對象來完成,代碼如下(這里只給出了片段代碼,只需把這代碼加入到上面插入數(shù)據(jù)的完整例子代碼中就可以使用):

    public void getData() throws IOException{
        Get g = new Get(Bytes.toBytes("rk_100001"));
        Result r = table.get(g);
        byte[] value = r.getValue(Bytes.toBytes("data"), Bytes.toBytes("uname"));
        String valueStr = Bytes.toString(value);
        System.out.println("uname:"+valueStr);
        value = r.getValue(Bytes.toBytes("data"), Bytes.toBytes("age"));
        System.out.println("age:"+Bytes.toString(value));
    }

上面代碼根據(jù)表的鍵值創(chuàng)建Get對象,然后利用Table對象獲取到結果集,最后通過結果集獲取各單元值。

(三)掃描表

在某項場景下,我們不知道鍵值的時候,可以利用Scan對象掃描整個表,遍歷所需的數(shù)據(jù)。例子如:

public void scanData() throws IOException{
    Scan s = new Scan();
    ResultScanner scanner = table.getScanner(s);
    try {
         for (Result rr : scanner) {
             byte[] row = rr.getRow();
             byte[] uname = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("uname"));
             byte[] age = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("age"));
             System.out.println("Found:" + Bytes.toString(row)+","+Bytes.toString(uname)+","+Bytes.toString(age));
         }
    } finally {
        scanner.close();
    }
}

上面代碼對整個表進行掃描。我們也可以給Scan對象增加條件,只獲取指定的列族或指定列的數(shù)據(jù)。

如果指定獲取列族的數(shù)據(jù),可以如下面代碼:

public void scanData() throws IOException{
    Scan s = new Scan();
    s.addFamily(Bytes.toBytes("data"));
    ResultScanner scanner = table.getScanner(s);
    try {
         for (Result rr : scanner) {
             byte[] row = rr.getRow();
             byte[] uname = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("uname"));
             byte[] age = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("age"));
             System.out.println("Found:" + Bytes.toString(row)+","+Bytes.toString(uname)+","+Bytes.toString(age));
         }
    } finally {
        scanner.close();
    }
}

上面代碼,調用Scan對象的addFamily方法限定只獲取指定列族的數(shù)據(jù),可以調用多次,增加多個列族。

我們也可以指定到具體的列,如下面代碼:

public void scanData() throws IOException{
    Scan s = new Scan();
    s.addColumn(Bytes.toBytes("data"), Bytes.toBytes("uname"));
    s.addColumn(Bytes.toBytes("data"), Bytes.toBytes("age"));
    ResultScanner scanner = table.getScanner(s);
    try {
         for (Result rr : scanner) {
             byte[] row = rr.getRow();
             byte[] uname = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("uname"));
             byte[] age = rr.getValue(Bytes.toBytes("data"), Bytes.toBytes("age"));
             System.out.println("Found:" + Bytes.toString(row)+","+Bytes.toString(uname)+","+Bytes.toString(age));
         }
    } finally {
        scanner.close();
    }
}

(四)獲取表的列族信息

有時我們可能需要通過代碼獲取hbase表定義的列族信息,例子代碼如下:

public void getCfInfo() throws IOException{
    ColumnFamilyDescriptor[] columnFamilyDescriptors = table.getDescriptor().getColumnFamilies();
    for (ColumnFamilyDescriptor columnFamilyDescriptor : columnFamilyDescriptors) {
        String familyName = columnFamilyDescriptor.getNameAsString();
        System.out.println(familyName);
    }
}

(五)刪除表中數(shù)據(jù)

我們可以按照鍵值來刪除數(shù)據(jù),如下面代碼。

public void deleteData() throws IOException{
        String[] rowKeys={"row1","row3"};
        List<Delete> deleteList = new ArrayList<>(rowKeys.length);
        Delete delete;
        for (String rowKey : rowKeys) {
            delete = new Delete(Bytes.toBytes(rowKey));
            deleteList.add(delete);
        }
        table.delete(deleteList);
    }

三、創(chuàng)建和更新表結構

我們可以編寫java代碼創(chuàng)建表、刪除表、修改表的結構,類似關系數(shù)據(jù)庫的DDL語句。關鍵的Api是Admin接口。

(一)查看所有的表

類似hbase命令行中l(wèi)ist命令,顯示當前用戶下所有的hbase表,例子代碼如下。

package com.hbaseexample;
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
public class AdminExample {
    private Connection connection = null;
    private Admin admin = null;
    public static void main(String[] args) {
        AdminExample example = new AdminExample();
        try {
            example.conn();
            example.listTable();
            System.out.println("finish");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            example.close();
        }
    }
    public void conn() throws IOException {
        Configuration config = HBaseConfiguration.create();
        config.set("hbase.zookeeper.quorum", "127.0.0.1");//zk地址
        config.set("hbase.zookeeper.property.clientPort", "2181");//zk端口
        connection = ConnectionFactory.createConnection(config);
        admin = connection.getAdmin();
    }
    public void close() {
        if (admin != null)
            try {admin.close(); } catch (IOException e) {}
        if (connection != null)
            try {connection.close();} catch (IOException e) {}
    }
    public void listTable() throws IOException{
        TableName[] names = admin.listTableNames();
        for(TableName name:names){
            System.out.println(name.getNameAsString());
        }
    }
}

上面代碼是一個完整的帶main方法的java類,首先獲得一個Connection對象,然后獲取到Admin對象,然后調用Admin對象的方法。下面的例子都是在上面代碼基礎上運行。

(二)創(chuàng)建/刪除表

下面代碼演示了檢查一個表是否存在,如果存在,則先使之無效,再刪除,然后創(chuàng)建一個新表。代碼如下(不是完整代碼,可放到上面完整例子代碼中運行):

public void doDDL() throws IOException {
    TableName tableName = TableName.valueOf("test");
    boolean re = admin.tableExists(tableName );
    if(re){
        admin.disableTable(tableName);
        admin.deleteTable(tableName);
    }
    TableDescriptorBuilder tableDesc = TableDescriptorBuilder.newBuilder(tableName);
    tableDesc.setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf1")).build());
    tableDesc.setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf2")).build());
    admin.createTable(tableDesc.build());
}

(三)刪除表的列族

例子代碼如下

public void deleteCF() throws IOException{
    TableName tableName = TableName.valueOf("test");
    admin.deleteColumnFamily(tableName, Bytes.toBytes("cf2"));
}

(四)添加表的列族

例子代碼如下

public void addCF() throws IOException{
    TableName tableName = TableName.valueOf("test");
    ColumnFamilyDescriptor cf =ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf2")).build();
    admin.addColumnFamily(tableName, cf );
    cf =ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf3")).build();
    admin.addColumnFamily(tableName, cf );
}

(五)修改已存在的列族

例子代碼如下

public void modifyCF() throws IOException{
    TableName tableName = TableName.valueOf("test");
    ColumnFamilyDescriptorBuilder builder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf1"));
    builder.setMaxVersions(3);
    admin.modifyColumnFamily(tableName, builder.build());
}

創(chuàng)建列族時,默認列族支持的最多存儲的版本數(shù)是1,上面例子代碼將列族cf1支持的最多存儲版本數(shù)改為3。

四、編譯和運行

上面的例子只是給出了程序的源代碼,那怎么編譯和執(zhí)行呢?因為代碼中引入了很多Hbase JAVA API的接口和類,需要引入相應的jar包。編寫的java hbase客戶端程序所依賴的jar包都位于hbase安裝目錄的lib目錄下。
想要成功編譯上面代碼,只需引入如下三個jar包即可(均在lib目錄下可找到):

hadoop-common-2.7.7.jar
hbase-common-2.0.2.jar
hbase-client-2.0.2.jar

但要想運行程序,需要依賴更多的jar包。這有兩種方式:
1、將lib根目錄下所有的jar包加入到classpath路徑中(如果在ide中運行,則全部引入),這樣可以在IDE(如eclipse)中直接運行。

2、把程序編譯后的class打成jar包。然后利用hbase提供的命令行客戶端腳本程序hbase來執(zhí)行,這樣就不需要關心要引入哪些jar包了。
執(zhí)行hbase腳本前,需要先將打成的jar包加入到HBASE_CLASSPATH環(huán)境變量中。然后運行hbase,后面跟著可執(zhí)行類名。如在linux下,在控制臺下執(zhí)行如下兩條語句即可:

export HBASE_CLASSPATH= 上面打成的jar包
hbase com.hbaseexample.Example

如在windows下,在dos命令行執(zhí)行,需要將export命令改成set命令。

五、遠程訪問hbase

如果我們希望編寫的hbase客戶端程序(位于windows系統(tǒng)下)能操作安裝在遠程的Linux機器上的hbase服務。這時需要做一些額外的配置。
當然前提是hbase客戶端程序將運行的機器上已經(jīng)有hbase客戶端可運行的環(huán)境。

需要做如下配置:
1、配置遠程hbase服務器支持遠程連接。這時需要在hbase-site.xml中添加一個屬性配置,屬性名 hbase.zookeeper.quorum ,屬性值為遠程服務器主機的主機名(注意,一定不是IP地址,而是主機名)。如下面配置例子:

  <property>
    <name>hbase.zookeeper.quorum</name>
    <value>master</value>
  </property>

上面的master是遠程服務器的主機名,不能配置成IP地址。

2、修改hbase客戶端程所在的windows機器上的配置文件:C:\Windows\System32\drivers\etc\hosts 在其中添加遠程Linux機器IP地址和主機名的對應關系。如:
192.168.2.6 master
上面的192.168.2.6是遠程Linux主機的IP地址,master是遠程Linux主機的主機名,要與hbase-site.xml中的配置一致。

3、修改java代碼
將上面例子中的代碼中的如下語句
config.set("hbase.zookeeper.quorum", "127.0.0.1");
改成如下的語句
config.set("hbase.zookeeper.quorum", "192.168.2.6");
上面的192.168.2.6是Linux服務器的IP地址,也可以用主機名master來替換IP地址,如:
config.set("hbase.zookeeper.quorum", "master");
經(jīng)過上面的設置后,java程序在windows下運行,就可以連接運行在遠程linux上的hbase服務了。

下面簡單介紹下linux下查看和修改主機名的方法,我們這里用的是版本是Centos7的linux操作系統(tǒng)。方法如下:

1、查看主機名的命令 hostname,默認情況下,Linxu的主機名叫l(wèi)ocalhost.localdomain

2、修改主機名,可以臨時修改(機器重啟將無效),或永久生效。
臨時修改只需執(zhí)行如下命令:
sudo hostname 設置的主機名
如果要永久生效,就需要修改Linux下的配置文件** /etc/hostname**,將其中的內容改成要設置的主機名,如master。根據(jù)需要,我們還可以修改 /etc/hosts文件,添加IP地址與主機名的對應關系。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 簡介 HBase是高可靠性,高性能,面向列,可伸縮的分布式存儲系統(tǒng),利用HBase技術可在廉價PC Serve...
    九世的貓閱讀 2,373評論 1 6
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,545評論 19 139
  • 一、簡介 Hbase:全名Hadoop DataBase,是一種開源的,可伸縮的,嚴格一致性(并非最終一致性)的分...
    菜鳥小玄閱讀 2,603評論 0 12
  • Zookeeper用于集群主備切換。 YARN讓集群具備更好的擴展性。 Spark沒有存儲能力。 Spark的Ma...
    Yobhel閱讀 7,602評論 0 34
  • 會哭孩子有糖吃,那懂事孩子就應該那樣被對待嗎?
    汐滺閱讀 190評論 0 0

友情鏈接更多精彩內容