HBase 管理 API: HBaseAdmin (HBase DDL)

類似於客戶端 API, HBase 也有用於管理任務的 API。管理 API 與 RDBMS 的 Data Definition Language (DDL) 類似,而客戶端 API 更類似於 Data Manipulation Language (DML).

管理 API 提供了各種數據管理操作功能:通過列族創建表,檢查表是否存在,修改表和列族定義,刪除表等等。下面將其提供的功能按操作相關性分組討論。

 

2.1 基本操作 (Basic Operations)
-----------------------------------------------------------------------------------------------------------------------------------------
在使用管理 API 之前,需要創建一個 Admin 接口實現的實例。不能直接創建該接口實例,要通過與 table 一樣的方式,通過 Connection 類的方法獲取一個
Admin 實例:

    Configuration conf = HBaseConfiguration.create();
    Connection connection = ConnectionFactory.createConnection(conf);
    Admin admin = connection.getAdmin();
    ...
    TableName[] tables = admin.listTableNames();
    ...
    admin.close();
    connection.close();

傳入已有的配置實例提供了足夠的配置信息給 API 來通過 ZooKeeper quorum 找到集羣,就如客戶端 API 所做的一樣。使用管理 API 實例進行必要的操作,
並且應該在使用後銷燬。換句話說,不要長期持有 Admin 實例。使用完之後應調用 close() 方法釋放在通信兩端佔用的資源。

Admin 繼承了 Abortable 接口,因此有如下方法:

    void abort(String why, Throwable e)
    boolean isAborted()

這兩個方法由框架隱式調用,例如,當發生致命的連接錯誤或關閉集羣時。用戶不應直接調用該方法,而要依賴於系統的調用。

Admin 接口還提供瞭如下兩基本調用:

    Connection getConnection()
    void close()

getConnection() 返回當前 Connection 實例。close() 方法釋放當前 Admin 實例持有的資源。

 

2.2 名稱空間操作 (Namespace Operations)
-----------------------------------------------------------------------------------------------------------------------------------------
可以利用管理 API 創建名稱空間,用於持有之後分配給它的表。並且可以修改或刪除已存在的名稱空間,以及獲取名稱空間描述符,方法如下:

    void createNamespace(final NamespaceDescriptor descriptor)
    void modifyNamespace(final NamespaceDescriptor descriptor)
    void deleteNamespace(final String name)
    NamespaceDescriptor getNamespaceDescriptor(final String name)
    NamespaceDescriptor[] listNamespaceDescriptors()

示例: Example using the administrative API to create etc. a namespace

    Configuration conf = HBaseConfiguration.create();
    Connection connection = ConnectionFactory.createConnection(conf);
    Admin admin = connection.getAdmin();

    NamespaceDescriptor namespace = NamespaceDescriptor.create("testspace").build();
    admin.createNamespace(namespace);

    NamespaceDescriptor namespace2 = admin.getNamespaceDescriptor("testspace");
    System.out.println("Simple Namespace: " + namespace2);

    NamespaceDescriptor[] list = admin.listNamespaceDescriptors();
    for (NamespaceDescriptor nd : list) {
        System.out.println("List Namespace: " + nd);
    }

    NamespaceDescriptor namespace3 = NamespaceDescriptor.create("testspace")
                                    .addConfiguration("Description", "Test Namespace")
                                    .build();
                                    
    admin.modifyNamespace(namespace3);

    NamespaceDescriptor namespace4 = admin.getNamespaceDescriptor("testspace");
    System.out.println("Custom Namespace: " + namespace4);
    
    admin.deleteNamespace("testspace");
    NamespaceDescriptor[] list2 = admin.listNamespaceDescriptors();
    for (NamespaceDescriptor nd : list2) {
        System.out.println("List Namespace: " + nd);
    }

輸出:
    Simple Namespace: {NAME => 'testspace'}
    List Namespace: {NAME => 'default'}
    List Namespace: {NAME => 'hbase'}
    List Namespace: {NAME => 'testspace'}
    Custom Namespace: {NAME => 'testspace', Description => 'Test Namespace'}
    List Namespace: {NAME => 'default'}
    List Namespace: {NAME => 'hbase'}


    
2.3 表操作 (Table Operations)
-----------------------------------------------------------------------------------------------------------------------------------------
這組調用是 HBase table 相關的。這些方法幫助表本身工作,而非內部的實際模式(actual schema)。在 HBase 開始任何工作之前,首先需要創建表:

    void createTable(HTableDescriptor desc)
    void createTable(HTableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions)
    void createTable(final HTableDescriptor desc, byte[][] splitKeys)
    void createTableAsync(final HTableDescriptor desc, final byte[][] splitKeys)

所有這些方法必須給定一個 HTableDescriptor 實例,它持有所創建表的詳細信息,包括列族。

示例: Example using the administrative API to create a table

    Configuration conf = HBaseConfiguration.create();
    Connection connection = ConnectionFactory.createConnection(conf);
    Admin admin = connection.getAdmin();
    
    TableName tableName = TableName.valueOf("testtable");
    HTableDescriptor desc = new HTableDescriptor(tableName);
    HColumnDescriptor coldef = new HColumnDescriptor(Bytes.toBytes("colfam1"));
    desc.addFamily(coldef);
    admin.createTable(desc);
    
    boolean avail = admin.isTableAvailable(tableName);
    System.out.println("Table available: " + avail);

示例: Example using the administrative API to create a table with a custom namespace

    NamespaceDescriptor namespace = NamespaceDescriptor.create("testspace").build();
    admin.createNamespace(namespace);

    TableName tableName = TableName.valueOf("testspace", "testtable");
    HTableDescriptor desc = new HTableDescriptor(tableName);
    HColumnDescriptor coldef = new HColumnDescriptor(Bytes.toBytes("colfam1"));
    desc.addFamily(coldef);

    admin.createTable(desc);

示例: Example using the administrative API to create a table with predefined regions

    private static Configuration conf = null;
    private static Connection connection = null;
    
    private static void printTableRegions(String tableName) throws IOException
    {
        System.out.println("Printing regions of table: " + tableName);
        TableName tn = TableName.valueOf(tableName);
        RegionLocator locator = connection.getRegionLocator(tn);
        
        //Retrieve the start and end keys from the newly created table.
        Pair<byte[][], byte[][]> pair = locator.getStartEndKeys();
        for (int n = 0; n < pair.getFirst().length; n++) {
            byte[] sk = pair.getFirst()[n];
            byte[] ek = pair.getSecond()[n];
            System.out.println("[" + (n + 1) + "]" +
            " start key: " +
            //Print the key, but guarding against the empty start (and end) key.
            (sk.length == 8 ? Bytes.toLong(sk) : Bytes.toStringBinary(
            sk)) +
            ", end key: " +
            (ek.length == 8 ? Bytes.toLong(ek) : Bytes.toStringBinary(
            ek)));
        }
        locator.close();
    }
    
    public static void main(String[] args) throws IOException, InterruptedException
    {
        conf = HBaseConfiguration.create();
        connection = ConnectionFactory.createConnection(conf);
        Admin admin = connection.getAdmin();
        
        HTableDescriptor desc = new HTableDescriptor(TableName.valueOf("testtable1"));
        HColumnDescriptor coldef = new HColumnDescriptor(Bytes.toBytes("colfam1"));
        desc.addFamily(coldef);
        admin.createTable(desc, Bytes.toBytes(1L), Bytes.toBytes(100L), 10);
        
        printTableRegions("testtable1");
        
        //Manually create region split keys.
        byte[][] regions = new byte[][] {
            Bytes.toBytes("A"),
            Bytes.toBytes("D"),
            Bytes.toBytes("G"),
            Bytes.toBytes("K"),
            Bytes.toBytes("O"),
            Bytes.toBytes("T")
        };
        
        HTableDescriptor desc2 = new HTableDescriptor(TableName.valueOf("testtable2"));
        desc2.addFamily(coldef);
        
        //Call the crateTable() method again, with a new table name and the list of region split keys
        admin.createTable(desc2, regions);
        printTableRegions("testtable2");
    }

輸出:
    Printing regions of table: testtable1
    [1] start key: , end key: 1
    [2] start key: 1, end key: 13
    [3] start key: 13, end key: 25
    [4] start key: 25, end key: 37
    [5] start key: 37, end key: 49
    [6] start key: 49, end key: 61
    [7] start key: 61, end key: 73
    [8] start key: 73, end key: 85
    [9] start key: 85, end key: 100
    [10] start key: 100, end key:
    Printing regions of table: testtable2
    [1] start key: , end key: A
    [2] start key: A, end key: D
    [3] start key: D, end key: G
    [4] start key: G, end key: K
    [5] start key: K, end key: O
    [6] start key: O, end key: T
    [7] start key: T, end key:

例子中使用了 RegionLocator 實現的方法 getStartEndKeys(), 獲取 region 邊界。第一個 region 的起始鍵,和最後一個 region 的結束鍵爲空,這是
HBase region 的慣用做法。中間 region 的鍵要麼是計算得來的,要麼由拆分的鍵(split keys)提供。注意,前一個 region 的結束鍵也是下一個
region 的起始鍵,這是由於結束鍵是不包含的,而起始鍵是包含的。

createTable(HTableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions) 方法接受一個起始鍵和一個結束鍵,被解釋爲數字,提供的
起始鍵必須必小於結束鍵,並且 numRegions 值至少爲 3,否則調用會返回異常。這樣才能確保以最小的 region 集合結束(end up with at least a
minimum set of regions).

起始鍵和結束鍵被減掉,併除以給定的 region 數量計算得到 region 邊界。

createTable(HTableDescriptor desc, byte[][] splitKeys) 方法用在示例的第二部分,它接受一個設置好的拆分鍵的數組:它們構成了創建的 region 的
起始鍵和結束鍵。

    NOTE
    -------------------------------------------------------------------------------------------------------------------------------------
    實際上,createTable() 調用之間是有聯繫的。createTable(HTableDescriptor desc, byte[] startKey, byte[] endKey, int numRegions) 方法爲
    用戶隱式計算 region keys, 將給定的參數通過Bytes.split() 方法計算出邊界。然後調用createTable(HTableDescriptor desc, byte[][] splitKeys)
    進行實際的表創建操作。

最後,createTableAsync(HTableDescriptor desc, byte[][] splitKeys) 接受表描述符實例和分區鍵(region keys) 作爲參數,異步執行創建表的任務。

-----------------------------------------------------------------------------------------------------------------------------------------
創建表之後,可以使用如下的輔助方法獲取 table 的列表,獲取某個 table 的描述符,或者檢查一個表是否存在:

    HTableDescriptor[] listTables()
    HTableDescriptor[] listTables(Pattern pattern)
    HTableDescriptor[] listTables(String regex)
    HTableDescriptor[] listTables(Pattern pattern, boolean includeSysTables)
    HTableDescriptor[] listTables(String regex, boolean includeSysTables)
    HTableDescriptor[] listTableDescriptorsByNamespace(final String name)
    HTableDescriptor getTableDescriptor(final TableName tableName)
    HTableDescriptor[] getTableDescriptorsByTableName(List<TableName> tableNames)
    HTableDescriptor[] getTableDescriptors(List<String> names)
    boolean tableExists(final TableName tableName)

示例: Example listing the existing tables and their descriptors

    Connection connection = ConnectionFactory.createConnection(conf);
    Admin admin = connection.getAdmin();
    HTableDescriptor[] htds = admin.listTables();
    for (HTableDescriptor htd : htds) {
        System.out.println(htd);
    }

    HTableDescriptor htd1 = admin.getTableDescriptor(
    TableName.valueOf("testtable1"));
    System.out.println(htd1);

    HTableDescriptor htd2 = admin.getTableDescriptor(
    TableName.valueOf("testtable10"));
    System.out.println(htd2);

輸出:
    Printing all tables...
    'testtable1', {NAME => 'colfam1', DATA_BLOCK_ENCODING => 'NONE',
    BLOOMFILTER
    => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION
    => 'NONE',
    MIN_VERSIONS => '0', TTL => 'FOREVER', KEEP_DELETED_CELLS =>
    'FALSE',
    BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'},
    {NAME => 'colfam2', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER =>
    'ROW',
    REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE',
    MIN_VERSIONS => '0', TTL => 'FOREVER', KEEP_DELETED_CELLS =>
    'FALSE',
    BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'},
    {NAME => 'colfam3', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER =>
    'ROW',
    REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE',
    MIN_VERSIONS => '0', TTL => 'FOREVER', KEEP_DELETED_CELLS =>
    'FALSE',
    BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}
    ...
    Exception in thread "main"
    org.apache.hadoop.hbase.TableNotFoundException: testtable10
    at org.apache.hadoop.hbase.client.HBaseAdmin.getTableDescriptor(...)
    at admin.ListTablesExample.main(ListTablesExample.java:49)
    ...

其中有意思的是打印出異常的部分。示例中使用了一個不存在的表名展現了這樣的事實:必須使用一個存在的表名,或者將此類調用通過 try/catch 塊保護
起來,這樣可以非常優雅地處理異常。也可以先使用 tableExists() 調用來檢查一個表是否存在。但是要記住,HBase 是一個分佈式的系統,因此,建議對
任何情況都使用 try/catch 保護。

-----------------------------------------------------------------------------------------------------------------------------------------
其它 listTables() 調用結束不同數量的參數,可以使用一個正則表達式進行過濾,或者使用字符串類型,或者使用已編譯的 Pattern 實例。更進一步,可
以指示調用是否包括系統表,通過設置 includeSysTables 參數爲 true, 因其默認是不包括的。
    
示例: Example listing the existing tables with patterns
    
    HTableDescriptor[] htds = admin.listTables(".*");
    htds = admin.listTables(".*", true);
    htds = admin.listTables("hbase:.*", true);
    htds = admin.listTables("def.*:.*", true);
    htds = admin.listTables("test.*");
    Pattern pattern = Pattern.compile(".*2");
    htds = admin.listTables(pattern);
    htds = admin.listTableDescriptorsByNamespace("testspace1");

    
輸出:

    List: .*
    testspace1:testtable1
    testspace2:testtable2
    testtable3
    List: .*, including system tables
    hbase:meta
    hbase:namespace
    testspace1:testtable1
    testspace2:testtable2
    testtable3
    List: hbase:.*, including system tables
    hbase:meta
    hbase:namespace
    List: def.*:.*, including system tables
    testtable3
    List: test.*
    testspace1:testtable1
    testspace2:testtable2
    testtable3
    List: .*2, using Pattern
    testspace2:testtable2
    List by Namespace: testspace1
    testspace1:testtable1

-----------------------------------------------------------------------------------------------------------------------------------------    
下一組方法是圍繞表名處理的,而不是整個表描述符,方法如下:

    TableName[] listTableNames()
    TableName[] listTableNames(Pattern pattern)
    TableName[] listTableNames(String regex)
    TableName[] listTableNames(final Pattern pattern,
    final boolean includeSysTables)
    TableName[] listTableNames(final String regex,
    final boolean includeSysTables)
    TableName[] listTableNamesByNamespace(final String name)

示例:
    TableName[] names = admin.listTableNames(".*");
    names = admin.listTableNames(".*", true);
    names = admin.listTableNames("hbase:.*", true);
    names = admin.listTableNames("def.*:.*", true);
    names = admin.listTableNames("test.*");
    Pattern pattern = Pattern.compile(".*2");
    names = admin.listTableNames(pattern);
    names = admin.listTableNamesByNamespace("testspace1");

另一組表信息相關的方法如下:

    List<HRegionInfo> getTableRegions(final byte[] tableName)
    List<HRegionInfo> getTableRegions(final TableName tableName)

這組方法類似於之前提到的 RegionLocator, 但不是返回表的每個 region 複雜的 HRegionLocation 信息,而是返回較輕量的 HRegionInfo 記錄。區別是
HRegionInfo 只是關於 region 的信息,而 HRegionLocation 還包含該 region 所分配的 region server 信息。
-----------------------------------------------------------------------------------------------------------------------------------------
創建表之後,可以通過以下方法將其刪除:

    void deleteTable(final TableName tableName)
    HTableDescriptor[] deleteTables(String regex)
    HTableDescriptor[] deleteTables(Pattern pattern)

傳入一個表名,其結果要注意:表從服務器上被移除,其所有數據都刪除。要非常小心,不要使用錯誤的正則表達式模式刪除錯誤的表。返回的數組是基於
操作失敗的所有的表,換句話說,如果操作成功,返回的數組是空的,但不是 null.
    
另一個相關的方法調用是,不刪除表本身,而是從中移除所有的數據:

    public void truncateTable(final TableName tableName, final boolean preserveSplits)

由於表是可能已經增長了並拆分爲多個 region, preserveSplits 標記指出如何處理這些 region 的列表。truncate 事實上類似於 disable 和drop 調用,
然後再跟一個 create 操作,即重新創建表。在這一點上,preserveSplits 標記決定服務器是重建這個表有一個 region, 還是具有其之前所包含的 region.


-----------------------------------------------------------------------------------------------------------------------------------------
在刪除一個表之前,需要首先確保它是 disabled, 使用如下方法:

    void disableTable(final TableName tableName)
    HTableDescriptor[] disableTables(String regex)
    HTableDescriptor[] disableTables(Pattern pattern)
    void disableTableAsync(final TableName tableName)

禁用表,首先告知該表的每一個 region server 將任何未提交的數據刷寫到磁盤,關閉所有 region, 更新系統表以反映出這個表沒有任何 region 部署到
任何服務器上。對基於模式調用返回的描述符列表是所有操作失敗的表,如果全部成功禁用,返回的數組是空的(但不是 null).

    NOTE
    -------------------------------------------------------------------------------------------------------------------------------------
    將表設置爲禁用可能會花費較長的時間,甚至長達幾分鐘。這取決於服務器內存中有多少殘餘數據還沒有持久化到磁盤上。將一個 region 下線會先將
    內存中的數據寫入磁盤,如果用戶設置了較大的堆,將導致 region server 需要向磁盤寫入數 MB 設置數 GB 的數據。在負載繁重的系統上進行數據寫
    入時,多個進程間的競爭寫入磁盤,因而需要時間來完成操作。


-----------------------------------------------------------------------------------------------------------------------------------------
一旦禁用了表,但不刪除它,可以再次啓用,方法如下:

    void enableTable(final TableName tableName)
    HTableDescriptor[] enableTables(String regex)
    HTableDescriptor[] enableTables(Pattern pattern)
    void enableTableAsync(final TableName tableName)

這些調用在轉移表到其它可用 region 服務器時比較有用。

最後,有一組方法用於檢查表的狀態:

    boolean isTableEnabled(TableName tableName)
    boolean isTableDisabled(TableName tableName)
    boolean isTableAvailable(TableName tableName)
    boolean isTableAvailable(TableName tableName, byte[][] splitKeys)


示例: Example using the various calls to disable, enable, and check that status of a table

    Connection connection = ConnectionFactory.createConnection(conf);
    Admin admin = connection.getAdmin();

    TableName tableName = TableName.valueOf("testtable");
    HTableDescriptor desc = new HTableDescriptor(tableName);
    HColumnDescriptor coldef = new HColumnDescriptor(Bytes.toBytes("colfam1"));
    desc.addFamily(coldef);
    admin.createTable(desc);

    try {
        admin.deleteTable(tableName);
    } catch (IOException e) {
        System.err.println("Error deleting table: " + e.getMessage());
    }

    admin.disableTable(tableName);
    boolean isDisabled = admin.isTableDisabled(tableName);
    System.out.println("Table is disabled: " + isDisabled);

    boolean avail1 = admin.isTableAvailable(tableName);
    System.out.println("Table available: " + avail1);

    admin.deleteTable(tableName);
    boolean avail2 = admin.isTableAvailable(tableName);
    System.out.println("Table available: " + avail2);

    admin.createTable(desc);
    boolean isEnabled = admin.isTableEnabled(tableName);
    System.out.println("Table is enabled: " + isEnabled);

輸出結果類似:

    Creating table...
    Deleting enabled table...
    Error deleting table: org.apache.hadoop.hbase.TableNotDisabledException: testtable
    at org.apache.hadoop.hbase.master.HMaster.checkTableModifiable(...)
    ...
    Disabling table...
    Table is disabled: true
    Table available: true
    Deleting disabled table...
    Table available: false
    Creating table again...
    Table is enabled: true

試圖刪除一個 enabled table 時拋出了異常錯誤,告知要麼先 disable 該 table, 或者在應用中對異常進行處理。

也注意到即便 table 是 disabled 狀態,isTableAvailable() 調用也返回 true。也就是說,這個方法檢查的是 table 是否在物理上存在,而不管它是什麼
狀態。


-----------------------------------------------------------------------------------------------------------------------------------------
通過指定的 schema 創建表後,要修改表的結構,要麼刪除它然後用修改後的信息重建表,要麼通過如下方法改變(alter) table 的結構:

    void modifyTable(final TableName tableName, final HTableDescriptor htd)
    Pair<Integer, Integer> getAlterStatus(final TableName tableName)
    Pair<Integer, Integer> getAlterStatus(final byte[] tableName)

modifyTable() 方法只有異步模式,沒有同步版本。如果要確保修改已經傳播到所有的服務器上並相應地應用到服務器上,可以在客戶端代碼中循環調用
getAlterStatus() 方法,直到 schema 應用到所有的服務器和 region 上。該調用返回一對數字,其含義如下:


    Meaning of numbers returned by getAlterStatus() call
    +---------------+--------------------------------------------------------------------------------
    | Pair Member    | Description
    +---------------+---------------------------------------------------------------------------------
    | first            | Specifies the number of regions that still need to be updated
    +---------------+---------------------------------------------------------------------------------
    | second        | Total number of regions affected by the change
    +---------------+---------------------------------------------------------------------------------

與刪除表一樣,必須先禁用該表才能對其進行修改。

示例: Example modifying the structure of an existing table

    Admin admin = connection.getAdmin();
    TableName tableName = TableName.valueOf("testtable");
    HColumnDescriptor coldef1 = new HColumnDescriptor("colfam1");

    HTableDescriptor desc = new HTableDescriptor(tableName)
                            .addFamily(coldef1)
                            .setValue("Description", "Chapter 5 - ModifyTableExample: OriginalTable");

    admin.createTable(desc, Bytes.toBytes(1L), Bytes.toBytes(10000L), 50);

    HTableDescriptor htd1 = admin.getTableDescriptor(tableName);
    HColumnDescriptor coldef2 = new HColumnDescriptor("colfam2");
    htd1.addFamily(coldef2)
        .setMaxFileSize(1024 * 1024 * 1024L)
        .setValue("Description", "Chapter 5 - ModifyTableExample: Modified Table");
        
    admin.disableTable(tableName);
    admin.modifyTable(tableName, htd1);

    Pair<Integer, Integer> status = new Pair<Integer, Integer>() {{setFirst(50); setSecond(50);}};

    for (int i = 0; status.getFirst() != 0 && i < 500; i++) {
        status = admin.getAlterStatus(desc.getTableName());
        if (status.getSecond() != 0) {
            int pending = status.getSecond() - status.getFirst();
            System.out.println(pending + " of " + status.getSecond()
            + " regions updated.");
            Thread.sleep(1 * 1000l);
        } else {
            System.out.println("All regions updated.");
            break;
        }
    }

    if (status.getFirst() != 0) {
        throw new IOException("Failed to update regions after 500 seconds.");
    }

    admin.enableTable(tableName);
    HTableDescriptor htd2 = admin.getTableDescriptor(tableName);
    System.out.println("Equals: " + htd1.equals(htd2));
    System.out.println("New schema: " + htd2);

輸出結果類似:
    50 of 50 regions updated.
    Equals: true
    New schema: 'testtable', {TABLE_ATTRIBUTES => {MAX_FILESIZE =>
    '1073741824',
    METADATA => {'Description' => 'Chapter 5 - ModifyTableExample:
    Modified Table'}}, {NAME => 'colfam1', DATA_BLOCK_ENCODING =>
    'NONE',
    BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '1',
    COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => 'FOREVER',
    KEEP_DELETED_CELLS => 'FALSE', BLOCKSIZE => '65536', IN_MEMORY =>
    'false', BLOCKCACHE => 'true'}, {NAME => 'colfam2', DATA_
    BLOCK_ENCODING
    => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', COMPRESSION
    => 'NONE', VERSIONS => '1', TTL => 'FOREVER', MIN_VERSIONS =>
    '0',
    KEEP_DELETED_CELLS => 'FALSE', BLOCKSIZE => '65536', IN_MEMORY
    => 'false',
    BLOCKCACHE => 'true'}


2.4 Schema 操作 (Schema Operations)
-----------------------------------------------------------------------------------------------------------------------------------------
除了 modifyTable() 方法,Admin 接口爲當前 table 的 schema 提供了專用的方法。當然首先要保證要修改的表已禁用。方法如下:

    void addColumn(final TableName tableName, final HColumnDescriptor column)
    void deleteColumn(final TableName tableName, final byte[] columnName)
    void modifyColumn(final TableName tableName, final HColumnDescriptor descriptor)

可以添加、刪除或修改列。添加或修改一個列需要首先準備 HColumnDescriptor 實例,或者可以使用 getTableDescriptor() 方法獲取當前表的 schema,
然後在返回的 HTableDescriptor 實例上調用 getColumnFamilies() 獲取已存在的列。否則,需要爲刪除調用提供表名和可選的列名。所有這些方法都是
異步的。


    使用場景 Hush (Use Case: Hush)
    -------------------------------------------------------------------------------------------------------------------------------------
    管理 API 一個有趣的應用場景是基於一個外部配置文件來創建表,修改表及其 schema. Hush 就是基於這種想法,在一個 XML 文件中定義表和列描述符
    從該文件中讀取表的定義,並將其與當前表的定義進行比較。如果有不同會依據文件中的定義應用到當前表中。下面示例給出了執行這個任務的核心代碼

    示例: Creating or modifying table schemas using the HBase administrative API
    
    private void createOrChangeTable(final HTableDescriptor schema)throws IOException {
        
        HTableDescriptor desc = null;
        if (tableExists(schema.getTableName(), false)) {
            desc = getTable(schema.getTableName(), false);
            LOG.info("Checking table " + desc.getNameAsString() + "...");
            final List<HColumnDescriptor> modCols = new ArrayList<HColumnDescriptor>();
            
            for (final HColumnDescriptor cd : desc.getFamilies()) {
                final HColumnDescriptor cd2 = schema.getFamily(cd.getName());
                //Compute the differences between the XML based schema and what is currently in HBase.
                if (cd2 != null && !cd.equals(cd2)) {
                    modCols.add(cd2);
            }
        }
        
        final List<HColumnDescriptor> delCols = new ArrayList<HColumnDescriptor>(desc.getFamilies());
        delCols.removeAll(schema.getFamilies());
        final List<HColumnDescriptor> addCols = new ArrayList<HColumnDescriptor>(schema.getFamilies());
        addCols.removeAll(desc.getFamilies());
        
        //See if there are any differences in the column and table definitions.
        if (modCols.size() > 0 || addCols.size() > 0 || delCols.size() > 0 ||!hasSameProperties(desc, schema)) {
            LOG.info("Disabling table...");
            admin.disableTable(schema.getTableName());
            
            if (modCols.size() > 0 || addCols.size() > 0 || delCols.size() > 0) {
                for (final HColumnDescriptor col : modCols) {
                    LOG.info("Found different column -> " + col);
                    //Alter the columns that have changed. The table was properly disabled first.
                    admin.modifyColumn(schema.getTableName(), col);
                }
                
                for (final HColumnDescriptor col : addCols) {
                    LOG.info("Found new column -> " + col);
                    //Add newly defined columns
                    admin.addColumn(schema.getTableName(), col);
                }
                for (final HColumnDescriptor col : delCols) {
                    LOG.info("Found removed column -> " + col);
                    //Delete removed columns
                    admin.deleteColumn(schema.getTableName(), col.getName());
                }
            } else if (!hasSameProperties(desc, schema)) {
                LOG.info("Found different table properties...");
                //Alter the table itself, if there are any differences found
                admin.modifyTable(schema.getTableName(), schema);
            }
                LOG.info("Enabling table...");
                admin.enableTable(schema.getTableName());
                LOG.info("Table enabled");
                getTable(schema.getTableName(), false);
                LOG.info("Table changed");
            } else {
                LOG.info("No changes detected!");
            }
        } else {
            LOG.info("Creating table " + schema.getNameAsString() +
            "...");
            //In case the table did not exist yet create it now.
            admin.createTable(schema);
            LOG.info("Table created");
        }
    }

    
2.5 集羣操作 (Cluster Operations)
-----------------------------------------------------------------------------------------------------------------------------------------
Admin 接口實現爲 region 和 table 本身的操作提供了方法。集羣操作被劃分爲 region, table 以及 server 組。

 

2.5.1 Region Operations
-----------------------------------------------------------------------------------------------------------------------------------------
region-related 操作,涉及 region 的狀態。


    NOTE
    -------------------------------------------------------------------------------------------------------------------------------------
    下面很多方法時爲高級用戶準備的,因此要仔細處理。
    
    
    ● List<HRegionInfo> getOnlineRegions(final ServerName sn)
    -------------------------------------------------------------------------------------------------------------------------------------
    一般在進行操作之前需要獲取一個 region 的列表,這個方法就用於此目的,返回一個給定服務器上持有的所有 region.

 

    void closeRegion(final String regionname, final String serverName)
    void closeRegion(final byte[] regionname, final String serverName)
    boolean closeRegionWithEncodedRegionName(final String en codedRegionName, final String serverName)
    void closeRegion(final ServerName sn, final HRegionInfo hri)
    -------------------------------------------------------------------------------------------------------------------------------------
    可以使用上述方法關閉之前已部署到 region 服務器上的 region。任何 enabled table 擁有的所有 region 也是 enabled, 因此可以關閉並取消部署
    其中的 region.

    使用上述方法,需要提供準確的 regionname, 它們存儲在系統表中。更進一步,可選地提供 serverName 參數,這會覆蓋系統表中找到的服務器分配。
    使用這些 close() 方法會繞過任何 master 通知,也就是說 region 由 region 服務器直接關閉,master 節點是看不見的。
    
    
    
    void flush(final TableName tableName)
    void flushRegion(final byte[] regionName)    
    -------------------------------------------------------------------------------------------------------------------------------------
    對 region 的更新(通常使用 table 更新)會累積更新到 region 服務器上的 MemStore 實例,MemStore 由未刷寫的修改填充。客戶端應用可以
    在達到 memstore 刷寫上限(memstore flush size)之前,通過這些同步方法顯式將 MemStore 中的數據記錄刷寫到磁盤。

    flush(final TableName tableName) 對給定 table 的所有 region 更新,flushRegion(final byte[] regionName) 對某個特定的 region 更新。


    void compact(final TableName tableName)
    void compact(final TableName tableName, final byte[] columnFamily)
    void compactRegion(final byte[] regionName)
    void compactRegion(final byte[] regionName, final byte[] columnFamily)
    void compactRegionServer(final ServerName sn, boolean major)
    -------------------------------------------------------------------------------------------------------------------------------------
    存儲文件不斷累積,系統會在後臺對它們進行合併壓縮(compaction),以保持文件數量在較低水平。通過這些調用,可以顯式觸發相同的操作,可以對
    整個服務器、一個表、或者一個特定的 region 執行操作。當給定一個列族名時,那麼操作只應用到那個列族上。設置 major 參數爲 true, 指示region
    服務器執行服務器範圍的 major 合併。
    
    調用本身是異步的,因爲 compaction 操作可能需要很長時間來完成。調用這些方法,將 table(s), region(s), 或 column family 合併操作排入隊列,
    由持有 region 的服務器在後臺執行,或者給定表的所有 region 所在的服務器執行。


    CompactionState getCompactionState(final TableName tableName)
    CompactionState getCompactionStateForRegion(final byte[] regionName)
    -------------------------------------------------------------------------------------------------------------------------------------
    這些方法時上一組方法的繼續,用於查詢運行 compaction 過程的狀態。可以請求整個表的狀態,也可以一個特定 region 的狀態。

 

    void majorCompact(TableName tableName)
    void majorCompact(TableName tableName, final byte[] columnFamily)
    void majorCompactRegion(final byte[] regionName)
    void majorCompactRegion(final byte[] regionName, final byte[] columnFamily)
    -------------------------------------------------------------------------------------------------------------------------------------
    這組方法與 compact() 調用類似,只是將 column family, region, 或 table 放入到一個 major compaction 隊列中。對給定 tableName 情況,管理
    API 會迭代該表的所有 region, 然後爲每一個 region 調用合併壓縮方法。


    void split(final TableName tableName)
    void split(final TableName tableName, final byte[] splitPoint)
    void splitRegion(final byte[] regionName)
    void splitRegion(final byte[] regionName, final byte[] splitPoint)
    -------------------------------------------------------------------------------------------------------------------------------------
    通過這些調用,可以拆分(split)一個特定的 region 或 table. 對於 table 作用域的調用,系統會迭代該表的所有 region, 並在每一個 region 上
    隱式調用拆分命令。

    有一點注意,當給定 splitPoint 參數時排除在上述規則之外。這種情況下,splite() 命令會嘗試在提供的 row key 位置處拆分給定的 region。如果
    是表範圍的調用,給定表的所有 region 都會被檢查,並將包含給定 splitPoint 的 region, 在 splitPoint row key 位置拆分。
    
    splitPoint 必須是一個有效的 row key, 並且在 region 範圍調用時,必須是要拆分的 region 的一部分。它也必須大於 region 的 startKey, 因爲在
    start key 位置拆分一個 region 是沒有意義的。如果沒有給定一個正確的 row key, 拆分請求(split request) 會被忽略,不會給客戶端返回任何報告。
    持有該 region 的服務器會在本地日誌中記錄如下消息:
    
    2015-04-12 20:39:58,077 ERROR [PriorityRpcServer.handler=4,queue=0,port=62255]     regionserver.HRegion: Ignoring invalid split
    org.apache.hadoop.hbase.regionserver.WrongRegionException: Requested row out of range for calculated split on HRegion testtable,,
    1428863984023.2d729d711208b37629baf70b5f17169c., startKey='', getEndKey()='ABC', row='ZZZ'
    at org.apache.hadoop.hbase.regionserver.HRegion.checkRow(HRegion.java)


    void mergeRegions(final byte[] encodedNameOfRegionA, final byte[] encodedNameOfRegionB, final boolean forcible)
    -------------------------------------------------------------------------------------------------------------------------------------
    這個方法用於將之前拆分的 region 合併。該操作通常要求指定相鄰的 region, 但設定 forcible 爲 true 會強制忽略這種安全性策略。

 

    void assign(final byte[] regionName)
    void unassign(final byte[] regionName, final boolean force)
    void offline(final byte[] regionName)
    -------------------------------------------------------------------------------------------------------------------------------------
    當客戶端要求一個 region 部署到 region server 上,或者從其 region server 上取消部署,可以調用這些方法。assign() 會分配一個 region, 基於
    整體分配計劃(the overall assignment plan), 而 unassign() 方法會將給定的 region 取消分配(unassign), 會觸發一次後續的自動分配。offline()
    調用會使一個 region 離線(offline), 也就是在調用之後,使該 region 處於取消分配狀態(unassigned)。

    force 參數設爲 true, 意思是如果 region 已經標記爲 unassigned, 例如,之前調用過 unassign(), 則會強制再一次取消分配。如果 force 設爲false
    則不會產生影響。
    
    
    void move(final byte[] encodedRegionName, final byte[] destServerName)
    -------------------------------------------------------------------------------------------------------------------------------------    
    使用 move() 方法,客戶端可以控制哪個服務器持有哪些 region。用戶可以將一個 region 從其當前的 region server 移動到一個新的 region server
    destServerName 參數可以設爲 null, 系統會隨機選擇一個 region server 作爲目標服務器,否則必須是一個有效的 region server。如果服務器名是
    錯誤的,或者它不響應操作,region 會被部署到一個不同的服務器上。最壞的情況,移動操作失敗,會將這個 region 置於未分配(unassigned) 狀態。
    
    destServerName 必須遵循 server name 規則,必須由主機名,端口,時間戳組件,具體規則參考前面論述:服務器和 region 名稱 (Server Names and
    Region Names)

    
    boolean setBalancerRunning(final boolean on, final boolean synchronous)
    boolean balancer()
    -------------------------------------------------------------------------------------------------------------------------------------
    第一個方法用於切換分區均衡器(region balancer) 開或關。當均衡器啓用時,balancer() 調用會啓動進程,將部分 region 從部署繁重的服務器上
    移動到部署較輕的服務器。synchronous 標誌用於控制方法的執行模式,爲 true 運行在同步模式,false 爲異步模式。
        

下面示例組合使用上述多種方法調用,展示管理 API 及其能力,在集羣範圍內修改數據佈局。

示例: Shows the use of the cluster operations

    Connection connection = ConnectionFactory.createConnection(conf);
    Admin admin = connection.getAdmin();

    TableName tableName = TableName.valueOf("testtable");
    HColumnDescriptor coldef1 = new HColumnDescriptor("colfam1");
    HTableDescriptor desc = new HTableDescriptor(tableName)
                            .addFamily(coldef1)
                            .setValue("Description", "Chapter 5 - ClusterOperationExample");
                            
    byte[][] regions = new byte[][] { Bytes.toBytes("ABC"),
                                    Bytes.toBytes("DEF"), Bytes.toBytes("GHI"), Bytes.toBytes("KLM"),
                                    Bytes.toBytes("OPQ"), Bytes.toBytes("TUV")
                                    };

    //Create a table with seven regions, and one column family.
    admin.createTable(desc, regions);

    BufferedMutator mutator = connection.getBufferedMutator(tableName);

    for (int a = 'A'; a <= 'Z'; a++)
        for (int b = 'A'; b <= 'Z'; b++)
            for (int c = 'A'; c <= 'Z'; c++) {
                
                //Insert many rows starting from “AAA” to “ZZZ”. These will be spread across the regions.
                String row = Character.toString((char) a) + Character.toString((char) b) + Character.toString((char)c);
                Put put = new Put(Bytes.toBytes(row));
                put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col1"), Bytes.toBytes("val1"));
                System.out.println("Adding row: " + row);
                mutator.mutate(put);
            }

    mutator.close();

    List<HRegionInfo> list = admin.getTableRegions(tableName);
    int numRegions = list.size();
    HRegionInfo info = list.get(numRegions - 1);

    //List details about the regions
    System.out.println("Number of regions: " + numRegions);
    System.out.println("Regions: ");
    printRegionInfo(list);
    System.out.println("Splitting region: " + info.getRegionNameAsString());

    //Split the last region this table has, starting at row key “TUV”. Adds a new region starting with key “WEI”
    admin.splitRegion(info.getRegionName());
    do {
        list = admin.getTableRegions(tableName);
        Thread.sleep(1 * 1000L);
        System.out.print(".");
    } while (list.size() <= numRegions); //Loop and check until the operation has taken effect.

    numRegions = list.size();
    System.out.println();
    System.out.println("Number of regions: " + numRegions);
    System.out.println("Regions: ");
    printRegionInfo(list);

    System.out.println("Retrieving region with row ZZZ...");
    RegionLocator locator = connection.getRegionLocator(tableName);

    //Retrieve region infos cached and refreshed to show the difference.
    HRegionLocation location = locator.getRegionLocation(Bytes.toBytes("ZZZ"));
    System.out.println("Found cached region: " + location.getRegionInfo().getRegionNameAsString());
    location = locator.getRegionLocation(Bytes.toBytes("ZZZ"), true);
    System.out.println("Found refreshed region: " + location.getRegionInfo().getRegionNameAsString());

    List<HRegionInfo> online = admin.getOnlineRegions(location.getServerName());
    online = filterTableRegions(online, tableName);
    int numOnline = online.size();
    System.out.println("Number of online regions: " + numOnline);
    System.out.println("Online Regions: ");
    printRegionInfo(online);

    HRegionInfo offline = online.get(online.size() - 1);
    System.out.println("Offlining region: " + offline.getRegionNameAsString());

    //Offline a region and print the list of all regions.
    admin.offline(offline.getRegionName());
    int revs = 0;
    do {
        online = admin.getOnlineRegions(location.getServerName());
        online = filterTableRegions(online, tableName);
        Thread.sleep(1 * 1000L);
        System.out.print(".");
        revs++;
    } while (online.size() <= numOnline && revs < 10);

    numOnline = online.size();
    System.out.println();
    System.out.println("Number of online regions: " + numOnline);
    System.out.println("Online Regions: ");
    printRegionInfo(online);

    //Attempt to split a region with a split key that does not fall into boundaries. Triggers log message.
    HRegionInfo split = online.get(0);
    System.out.println("Splitting region with wrong key: " + split.getRegionNameAsString());
    admin.splitRegion(split.getRegionName(), Bytes.toBytes("ZZZ")); // triggers log message
    System.out.println("Assigning region: " + offline.getRegionNameAsString());

    //Reassign the offlined region.
    admin.assign(offline.getRegionName());
    revs = 0;
    do {
        online = admin.getOnlineRegions(location.getServerName());
        online = filterTableRegions(online, tableName);
        Thread.sleep(1 * 1000L);
        System.out.print(".");
        revs++;
    } while (online.size() == numOnline && revs < 10);

    numOnline = online.size();
    System.out.println();
    System.out.println("Number of online regions: " + numOnline);
    System.out.println("Online Regions: ");
    printRegionInfo(online);

    System.out.println("Merging regions...");
    HRegionInfo m1 = online.get(0);
    HRegionInfo m2 = online.get(1);
    System.out.println("Regions: " + m1 + " with " + m2);

    //Merge the first two regions. Print out result of operation
    admin.mergeRegions(m1.getEncodedNameAsBytes(), m2.getEncodedNameAsBytes(), false);
    revs = 0;
    do {
        list = admin.getTableRegions(tableName);
        Thread.sleep(1 * 1000L);
        System.out.print(".");
        revs++;
    } while (list.size() >= numRegions && revs < 10);

    numRegions = list.size();
    System.out.println();
    System.out.println("Number of regions: " + numRegions);
    System.out.println("Regions: ");
    printRegionInfo(list);

 

2.5.2 表操作:快照 (Table Operations: Snapshots)
-----------------------------------------------------------------------------------------------------------------------------------------
這部分集羣操作圍繞真實的表進行操作。這些是低級別的任務,可以從管理 API 調用並應用到整個給定的表範圍。其主要的目的是解決一個表的當前狀態,
稱爲快照(snapshot). 下面是爲一個表創建快照的管理 API 方法:

    void snapshot(final String snapshotName, final TableName tableName)
    void snapshot(final byte[] snapshotName, final TableName tableName)
    void snapshot(final String snapshotName, final TableName tableName, Type type)
    void snapshot(SnapshotDescription snapshot)
    SnapshotResponse takeSnapshotAsync(SnapshotDescription snapshot)
    boolean isSnapshotFinished(final SnapshotDescription snapshot)
    
需要爲每個快照提供一個唯一的名稱,快照名稱遵循 table 的命名規則。這是因爲 snapshot 在底層文件系統上與 table 採用同樣的方式存儲,在一個特
定的位置上。可以通過 TableName.isLegalTableQualifierName() 方法驗證一個給定的 snapshot name 是否滿足要求。另外,需要指定要在其上執行快照的
table 的名稱。

有一個 Type 類型的參數,它指定要創建的快照的類型,其選項如下:

    Choices available for snapshot types
    +-----------+---------------+--------------------------------------------------------------------------
    | Type        | Table State    | Description
    +-----------+---------------+---------------------------------------------------------------------------
    | FLUSH        | Enabled        | This is the default and is used to force a flush operation on online tables
    |            |                | before the snapshot is taken
    +-----------+---------------+---------------------------------------------------------------------------
    | SKIPFLUSH    | Enabled        | If you do not want to cause a flush to occur, you can use this option to
    |            |                | immediately snapshot all persisted files of a table
    +-----------+---------------+---------------------------------------------------------------------------
    | DISABLED    | Disabled        | This option is not for normal use, but might be returned if a snapshot was
    |            |                | created on a disabled table
    +-----------+---------------+---------------------------------------------------------------------------

相同的枚舉類型也用於 listSnapshot() 返回的對象,這就是爲什麼 DISABLED 值是一個可能的快照類型:它取決於是什麼時候取得的快照,即創建快照
時,table 是 enabled 還是 disabled. 很明顯,在一個 disabled 的 table 上傳入 FLUSH 或 SKIPFLUSH 的類型不會有任何效果。相反,快照會完成創
建並在列出快照時以 DISABLED 類型返回,不管給它指定了什麼類型。

-----------------------------------------------------------------------------------------------------------------------------------------
一旦創建了一個或多個快照,就可以通過如下方法獲取到一個可用快照的列表:

    List<SnapshotDescription> listSnapshots()
    List<SnapshotDescription> listSnapshots(String regex)
    List<SnapshotDescription> listSnapshots(Pattern pattern)

第一個方法列出存儲的所有快照,另外兩個通過一個正則表達式模式過濾返回快照列表。輸出類似如下:

    [name: "snapshot1"
    table: "testtable"
    creation_time: 1428924867254
    type: FLUSH
    version: 2
    , name: "snapshot2"
    table: "testtable"
    creation_time: 1428924870596
    type: DISABLED
    version: 2]

listSnapshots() 返回一個 SnapshotDescription 實例的列表,可以通過它訪問快照的細節信息。SnapshotDescription 有 getName() 和 getTable()
方法返回 snapshot 和 table 的名稱。另外可以通過 getType() 方法訪問快照的類型,getCreationTime()獲取快照創建時的時間戳。最後,getVersion()
方法返回內部格式的 snapshot 版本號,這個版本號用於新版本的 HBase 讀取舊版本的快照,因此,這個版本號隨 HBase 的主版本(major version of
HBase)提升。SnapshotDescription 還有幾個 getter 和 setter 用於設置 snapshot 的信息,佔用的存儲容量,以及其它便利方法用於獲取其它格式的
描述信息。


-----------------------------------------------------------------------------------------------------------------------------------------
需要恢復一個以前取得的快照時,需要調用如下方法:

    void restoreSnapshot(final byte[] snapshotName)
    void restoreSnapshot(final String snapshotName)
    void restoreSnapshot(final byte[] snapshotName, final boolean takeFailSafeSnapshot)
    void restoreSnapshot(final String snapshotName, boolean takeFailSafeSnapshot)    

需要指定 snapshotName,會使用快照中包含的數據重建 table. 在對錶運行恢復操作之前,需要首先 disable 這個表。恢復操作基本上是一個 drop 操作,
然後使用存檔的數據重建該表。當然,快照必須存在,否則會收到一個錯誤。

可選的 takeFailSafeSnapshot 參數,如果爲 true, 指示服務器在恢復操作執行前,會首先對指定的表執行 snapshot() 操作。這樣,如果恢復操作失敗,
會使用這個破損安全快照(failsafe snapshot) 恢復。破損安全快照的名稱通過 hbase.snapshot.restore.failsafe.name 配置屬性指定,並且默認值爲
hbase-failsafe-{snapshot.name}-{restore.timestamp}。名稱中可用的變量爲:

    +-----------------------+--------------------------------------------------------------
    | Variable                | Description
    +-----------------------+--------------------------------------------------------------
    |{snapshot.name}        | The name of the snapshot
    +-----------------------+--------------------------------------------------------------
    |{table.name}            | The name of the table the snapshot represents
    +-----------------------+--------------------------------------------------------------
    |{restore.timestamp}    | The timestamp when the snapshot is taken
    +-----------------------+--------------------------------------------------------------

破損安全快照名稱的默認值,通過添加觸發其創建的快照名稱,加上一個時間戳保證了快照名的唯一性。沒有必要修改這個值。


-----------------------------------------------------------------------------------------------------------------------------------------
也可以克隆(cloneSnapshot)一個快照,含義是用一個新的名稱重建表:

    void cloneSnapshot(final byte[] snapshotName, final TableName tableName)
    void cloneSnapshot(final String snapshotName, final TableName tableName)

需要指定一個快照名稱,並且需要提供一個新的表名稱。快照以新命名的表恢復,與在原始表上恢復類似。

最後,通過如下方法實現對快照的移除:

    void deleteSnapshot(final byte[] snapshotName)
    void deleteSnapshot(final String snapshotName)
    void deleteSnapshots(final String regex)
    void deleteSnapshots(final Pattern pattern)

可以指定一個準確的快照名稱,或者應用一個正則表達式,在一次調用上移除多個快照。輸入時要小心,這個操作是不可恢復的。

示例: Example showing the use of the admin snapshot API

    //Create a snapshot of the initial table, then list all available snapshots next.
    admin.snapshot("snapshot1", tableName);

    List<HBaseProtos.SnapshotDescription> snaps = admin.listSnapshots();
    System.out.println("Snapshots after snapshot 1: " + snaps);

    Delete delete = new Delete(Bytes.toBytes("row1"));
    delete.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual1"));

    //Remove one column and do two more snapshots, one without first flushing, then another with a preceding flush.
    table.delete(delete);

    admin.snapshot("snapshot2", tableName, HBaseProtos.SnapshotDescription.Type.SKIPFLUSH);
    admin.snapshot("snapshot3", tableName, HBaseProtos.SnapshotDescription.Type.FLUSH);
    snaps = admin.listSnapshots();
    System.out.println("Snapshots after snapshot 2 & 3: " + snaps);

    //Add a new row to the table and take yet another snapshot
    Put put = new Put(Bytes.toBytes("row2"))
            .addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual10"), Bytes.toBytes("val10"));
    table.put(put);

    HBaseProtos.SnapshotDescription snapshotDescription = HBaseProtos.SnapshotDescription.newBuilder()
        .setName("snapshot4")
        .setTable(tableName.getNameAsString())
        .build();
    admin.takeSnapshotAsync(snapshotDescription);

    snaps = admin.listSnapshots();
    System.out.println("Snapshots before waiting: " + snaps);

    System.out.println("Waiting...");

    //Wait for the asynchronous snapshot to complete. List the snapshots before and after the waiting
    while (!admin.isSnapshotFinished(snapshotDescription)) {
        Thread.sleep(1 * 1000);
        System.out.print(".");
    }

    System.out.println();
    System.out.println("Snapshot completed.");
    snaps = admin.listSnapshots();
    System.out.println("Snapshots after waiting: " + snaps);
    System.out.println("Table before restoring snapshot 1");
    helper.dump("testtable", new String[]{"row1", "row2"}, null, null);

    admin.disableTable(tableName);
    //Restore the first snapshot, recreating the initial table. This needs to be done on a disabled table.
    admin.restoreSnapshot("snapshot1");
    admin.enableTable(tableName);
    System.out.println("Table after restoring snapshot 1");
    helper.dump("testtable", new String[]{"row1", "row2"}, null, null);

    //Remove the first snapshot, and list the available ones again.
    admin.deleteSnapshot("snapshot1");
    snaps = admin.listSnapshots();
    System.out.println("Snapshots after deletion: " + snaps);
    admin.cloneSnapshot("snapshot2", TableName.valueOf("testtable2"));
    System.out.println("New table after cloning snapshot 2");
    helper.dump("testtable2", new String[]{"row1", "row2"}, null, null);

    //Clone the second and third snapshot into a new table, dump the content to show the difference between the “skipflush” and “flush” types.
    admin.cloneSnapshot("snapshot3", TableName.valueOf("testtable3"));
    System.out.println("New table after cloning snapshot 3");
    helper.dump("testtable3", new String[]{"row1", "row2"}, null, null);

輸出類似如下:
    Before snapshot calls...
    Cell: row1/colfam1:qual1/2/Put/vlen=4/seqid=0, Value: val1
    Cell: row1/colfam1:qual1/1/Put/vlen=4/seqid=0, Value: val1
    ...
    Cell: row1/colfam2:qual3/6/Put/vlen=4/seqid=0, Value: val3
    Cell: row1/colfam2:qual3/5/Put/vlen=4/seqid=0, Value: val3
    Snapshots after snapshot 1: [name: "snapshot1"
    table: "testtable"
    creation_time: 1428918198629
    type: FLUSH
    version: 2
    ]
    Snapshots after snapshot 2 & 3: [name: "snapshot1"
    table: "testtable"
    creation_time: 1428918198629
    type: FLUSH
    version: 2
    , name: "snapshot2"
    table: "testtable"
    creation_time: 1428918200818
    type: SKIPFLUSH
    version: 2
    , name: "snapshot3"
    table: "testtable"
    creation_time: 1428918200931
    type: FLUSH
    version: 2
    ]
    Snapshots before waiting: [name: "snapshot1"
    table: "testtable"
    creation_time: 1428918198629
    type: FLUSH
    version: 2
    , name: "snapshot2"
    table: "testtable"
    creation_time: 1428918200818
    type: SKIPFLUSH
    version: 2
    , name: "snapshot3"
    table: "testtable"
    creation_time: 1428918200931
    type: FLUSH
    version: 2
    ]
    Waiting...
    .
    Snapshot completed.
    Snapshots after waiting: [name: "snapshot1"
    table: "testtable"
    creation_time: 1428918198629
    type: FLUSH
    version: 2
    , name: "snapshot2"
    table: "testtable"
    creation_time: 1428918200818
    type: SKIPFLUSH
    version: 2
    , name: "snapshot3"
    table: "testtable"
    creation_time: 1428918200931
    type: FLUSH
    version: 2
    , name: "snapshot4"
    table: "testtable"
    creation_time: 1428918201570
    version: 2
    ]
    Table before restoring snapshot 1
    Cell: row1/colfam1:qual1/1/Put/vlen=4/seqid=0, Value: val1
    Cell: row1/colfam1:qual2/4/Put/vlen=4/seqid=0, Value: val2
    ...
    Cell: row1/colfam2:qual3/5/Put/vlen=4/seqid=0, Value: val3
    Cell: row2/colfam1:qual10/1428918201565/Put/vlen=5/seqid=0, Value:
    val10
    Table after restoring snapshot 1
    Cell: row1/colfam1:qual1/2/Put/vlen=4/seqid=0, Value: val1
    Cell: row1/colfam1:qual1/1/Put/vlen=4/seqid=0, Value: val1
    ...
    Cell: row1/colfam2:qual3/6/Put/vlen=4/seqid=0, Value: val3
    Cell: row1/colfam2:qual3/5/Put/vlen=4/seqid=0, Value: val3
    Snapshots after deletion: [name: "snapshot2"
    table: "testtable"
    creation_time: 1428918200818
    type: SKIPFLUSH
    version: 2
    , name: "snapshot3"
    table: "testtable"
    creation_time: 1428918200931
    type: FLUSH
    version: 2
    , name: "snapshot4"
    table: "testtable"
    creation_time: 1428918201570
    version: 2
    ]
    New table after cloning snapshot 2
    Cell: row1/colfam1:qual1/2/Put/vlen=4/seqid=0, Value: val1
    Cell: row1/colfam1:qual1/1/Put/vlen=4/seqid=0, Value: val1
    Cell: row1/colfam1:qual2/4/Put/vlen=4/seqid=0, Value: val2
    ...
    Cell: row1/colfam2:qual3/6/Put/vlen=4/seqid=0, Value: val3
    Cell: row1/colfam2:qual3/5/Put/vlen=4/seqid=0, Value: val3
    New table after cloning snapshot 3
    Cell: row1/colfam1:qual1/1/Put/vlen=4/seqid=0, Value: val1
    Cell: row1/colfam1:qual2/4/Put/vlen=4/seqid=0, Value: val2
    Cell: row1/colfam1:qual2/3/Put/vlen=4/seqid=0, Value: val2
    ...
    Cell: row1/colfam2:qual3/6/Put/vlen=4/seqid=0, Value: val3
    Cell: row1/colfam2:qual3/5/Put/vlen=4/seqid=0, Value: val3


    關於快照的幾個注意事項
    -------------------------------------------------------------------------------------------------------------------------------------
    ● 每個表只能有一個快照創建或恢復操作在進行中。也就是說,有兩個不同的表,可以同時在這兩個表上進行快照創建操作,但不能在同一個表上併發
    執行兩個快照創建操作,或者在一個恢復操作正在進行的過程中,在該表上運行快照創建操作。第二個操作會失敗,並攜帶一個錯誤消息,例如:
        
        Rejected taking <snapshotname> because we are already running another snapshot...
        
    ● 可以提升創建快照操作的併發數量,默認值爲 1,由 hbase.snapshot.master.threads 配置屬性控制。默認值 1 的含義是在任何一個給定的時刻,在
    整個集羣上只有一個創建快照的操作執行。後面的操作會被放入隊列並且按順序執行。可以設置爲更高的值以提升併發數量。
    
    ● 禁用整個集羣的快照支持通過 hbase.snapshot.enabled 配置屬性控制。默認設爲 true, 即默認安裝的集羣,快照支持是啓用的。

 

2.5.3 服務器操作 (Server Operations)
-----------------------------------------------------------------------------------------------------------------------------------------
Admin 接口提供的這組方法時處理整個集羣的。有的是通用方法,有的是非常底層的操作,因此,要非常小心,清楚自己在做什麼。


    ClusterStatus getClusterStatus()
    -------------------------------------------------------------------------------------------------------------------------------------
    可以獲取 ClusterStatus 類實例,包含集羣狀態的詳細信息。

 

    Configuration getConfiguration()
    void updateConfiguration(ServerName server)
    void updateConfiguration()
    -------------------------------------------------------------------------------------------------------------------------------------
    這些調用允許應用訪問當前的配置,以及從磁盤重新載入配置信息。updateConfiguration() 重新載入所有服務器的配置信息,updateConfiguration(
    ServerName server) 則只載入給定服務器的配置。在服務器運行期間,不是所有的配置屬性都支持可重新載入。
    
    利用 getConfiguration() 可以訪問客戶端的配置實例。由於 HBase 是分佈式的系統,很可能客戶端的設置與服務端的設置不同。並且在返回的
    Configuration 實例上調用任何 set() 方法僅僅修改的是客戶端的設置。如果要更新服務器的配置,需要部署一個更新的 hbase-site.xml 文件到服務器
    並且調用 updateConfiguration() 方法。
    

    int getMasterInfoPort()
    -------------------------------------------------------------------------------------------------------------------------------------
    返回 HBase Master 當前的 web-UI 端口。這個值通過 hbase.master.info.port 屬性設置,但可以在服務器啓動時動態重新分配。
    
    
    int getOperationTimeout()
    -------------------------------------------------------------------------------------------------------------------------------------
    返回 hbase.client.operation.timeout 屬性的值。這個值定義了客戶端等待服務器響應多長時間,默認值爲 Integer.MAX_VALUE, 即無限期等待


    void rollWALWriter(ServerName serverName)
    -------------------------------------------------------------------------------------------------------------------------------------
    指示服務器關閉當前的 WAL 文件,並創建一個新的 WAL 文件。


    boolean enableCatalogJanitor(boolean enable)
    int runCatalogScan()
    boolean isCatalogJanitorEnabled()
    -------------------------------------------------------------------------------------------------------------------------------------
    HBase Master 進程運行一個後臺管理任務(background housekeeping task), catalog janitor, 負責清理 region 操作的剩餘部分。例如,在一個
    region 拆分或合併時,janitor 會清理剩餘的 region 細節信息,包括元數據和物理文件。默認情況下,任務運行在每個標準集羣上。可以通過上述
    調用停止任務的運行,通過 runCatalogScan() 手動運行。


    String[] getMasterCoprocessors()
    CoprocessorRpcChannel coprocessorService()
    CoprocessorRpcChannel coprocessorService(ServerName sn)
    -------------------------------------------------------------------------------------------------------------------------------------
    提供訪問載入到 master 進程中的協處理器列表,和 RPC channel(繼承自 Protobuf 超類)。

 

    void execProcedure(String signature, String instance, Map<String, String> props)
    byte[] execProcedureWithRet(String signature, String instance, Map<String, String> props)
    boolean isProcedureFinished(String signature, String instance, Map<String, String> props)
    -------------------------------------------------------------------------------------------------------------------------------------
    HBase 有一個服務器端的過程框架(procedure framework), 用於,例如,master 進程分派一個操作到幾個或所有的 region 服務器上。如果觸發了一個
    刷寫操作,這個過程表現爲在集羣上進行刷寫操作。


    void shutdown()
    void stopMaster()
    void stopRegionServer(final String hostnamePort)
    -------------------------------------------------------------------------------------------------------------------------------------
    這組調用要麼關閉整個集羣,停止 master server, 或者只是停止一個特定的 region server。一旦調用,受影響的服務器就會被停止。

 

2.6 集羣狀態信息 (Cluster Status Information)
-----------------------------------------------------------------------------------------------------------------------------------------
使用 Admin.getClusterStatus() 方法調用會返回一個 ClusterStatus 實例,其中包含有 master 服務器上關於集羣當前狀態的所有信息。下表列出該類
提供的方法:

    Overview of the information provided by the ClusterStatus class
    +---------------------------+-------------------------------------------------------------------------------------------
    | Method                    | Description
    +---------------------------+-------------------------------------------------------------------------------------------
    | getAverageLoad()            | The total average number of regions per region server. This is computed as
    |                            | number of regions/number of servers.
    +---------------------------+-------------------------------------------------------------------------------------------
    | getBackupMasters()        | Returns the list of all known backup HBase Master servers
    +---------------------------+-------------------------------------------------------------------------------------------
    | getBackupMastersSize()    | The size of the list of all known backup masters
    +---------------------------+-------------------------------------------------------------------------------------------
    | getBalancerOn()            | Provides access to the internal Boolean instance, reflecting the balancer
    |                            | tasks status. Might be null.
    +---------------------------+-------------------------------------------------------------------------------------------
    | getClusterId()            | Returns the unique identifier for the cluster. This is a UUID generated when
    |                            | HBase starts with an empty storage directory. It is stored in hbase.id under
    |                            | the HBase root directory.
    +---------------------------+-------------------------------------------------------------------------------------------
    | getDeadServerNames()        | A list of all server names currently considered dead. The names in the collection
    |                            | are ServerName instances, which contain the hostname, RPC port, and start code.
    +---------------------------+-------------------------------------------------------------------------------------------
    | getDeadServers()            | The number of servers listed as dead. This does not contain the live servers.
    +---------------------------+-------------------------------------------------------------------------------------------
    | getHBaseVersion()            | Returns the HBase version identification string
    +---------------------------+-------------------------------------------------------------------------------------------
    | getLoad(ServerName sn)    | Retrieves the status information available for the given server name
    +---------------------------+-------------------------------------------------------------------------------------------
    | getMaster()                | The server name of the current master
    +---------------------------+-------------------------------------------------------------------------------------------
    | getMasterCoprocessors()    | A list of all loaded master coprocessors.
    +---------------------------+-------------------------------------------------------------------------------------------
    | getRegionsCount()            | The total number of regions in the cluster
    +---------------------------+-------------------------------------------------------------------------------------------
    | getRegionsInTransition()    | Gives you access to a map of all regions currently in transition, e.g., being moved,
    |                            | assigned, or unassigned. The key of the map is the encoded region name (as returned by
    |                            | HRegionInfo.getEncodedName(), for example), while the value is an instance of RegionState
    +---------------------------+-------------------------------------------------------------------------------------------
    | getRequestsCount()        | The current number of requests across all region servers in the cluster
    +---------------------------+-------------------------------------------------------------------------------------------
    | getServers()                | The list of live servers. The names in the collection are ServerName instances, which
    |                            | contain the hostname, RPC port, and start code.
    +---------------------------+-------------------------------------------------------------------------------------------
    | getServersSize()            | The number of region servers currently live as known to the master server. The number
    |                            | does not include the number of dead servers
    +---------------------------+-------------------------------------------------------------------------------------------
    | getVersion()                | Returns the format version of the ClusterStatus instance. This is used during the
    |                            | serialization process of sending an instance over RPC
    +---------------------------+-------------------------------------------------------------------------------------------
    | isBalancerOn()            | Returns true if the balancer task is enabled on the master
    +---------------------------+-------------------------------------------------------------------------------------------
    | toString()                | Converts the entire cluster status details into a string
    +---------------------------+-------------------------------------------------------------------------------------------


訪問整個集羣狀態可以在較高層次上看到集羣的整體情況。通過 getServers() 方法返回的 ServerName 實例集合,可以更進一步查看實際活動服務器當前
的工作信息。

每個服務器,通過 ClusterStatus 實例的 getLoad() 方法返回的 ServerLoad 實例,提供其負載的詳細信息。利用 getServers() 返回 ServerName, 不但
可以迭代訪問所有活躍服務器本身的負載信息,而且可以訪問每個存儲的 region


    Overview of the information provided by the ServerLoad class
    +-----------------------------------+-----------------------------------------------------------------------------------
    | Method                            | Description
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getCurrentCompactedKVs()            | The number of cells that have been compacted, while compactions are running
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getInfoServerPort()                | The web-UI port of the region server
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getLoad()                            | Currently returns the same value as getNumberOfRegions().
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getMaxHeapMB()                    | The configured maximum Java Runtime heap size in megabytes.
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getMemStoreSizeInMB()                | The total size of the in-memory stores, across all regions hosted by this server
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getNumberOfRegions()                | The number of regions on the current server
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getNumberOfRequests()                | Returns the accumulated number of requests, and counts all API requests, such as
    |                                    | gets, puts, increments, deletes, and so on
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getReadRequestsCount()            | The sum of all read requests for all regions of this server
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getRegionServerCoprocessors()        | The list of loaded coprocessors, provided as a string array, listing the class names
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getRegionsLoad()                    | Returns a map containing the load details for each hosted region of the current server.
    |                                    | The key is the region name and the value an instance of the RegionsLoad class
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getReplicationLoadSink()            | If replication is enabled, this call returns an object with replication statistics
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getReplicationLoadSourceList()    | If replication is enabled, this call returns a list of objects with replication
    |                                    | statistics
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getRequestsPerSecond()            | Provides the computed requests per second value, accumulated for the entire server.
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getRootIndexSizeKB()                | The summed up size of all root indexes, for every storage file, the server holds
    |                                    | in memory
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getRsCoprocessors()                | The list of coprocessors in the order they were loaded. Should be equal to
    |                                    | getRegionServerCoprocessors().
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getStorefileIndexSizeInMB()        | The total size in megabytes of the indexes—the block and meta index, to be
    |                                    | precise—across all store files in use by this server
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getStorefiles()                    | The number of store files in use by the server. This is across all regions it hosts
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getStorefileSizeInMB()            | The total size in megabytes of the used store files
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getStores()                        | The total number of stores held by this server. This is similar to the number of
    |                                    | all column families across all regions
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getStoreUncompressedSizeMB()        | The raw size of the data across all stores in megabytes
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getTotalCompactingKVs()            | The total number of cells currently compacted across all stores
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getTotalNumberOfRequests()        | Returns the total number of all requests received by this serve
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getTotalStaticBloomSizeKB()        | Specifies the combined size occupied by all Bloom filters in kilobytes
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getTotalStaticIndexSizeKB()        | Specifies the combined size occupied by all indexes in kilobytes
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getUsedHeapMB()                    | The currently used Java Runtime heap size in megabytes, if available
    +-----------------------------------+-----------------------------------------------------------------------------------
    | getWriteRequestsCount()            | The sum of all read requests for all regions of this server
    +-----------------------------------+-----------------------------------------------------------------------------------
    | hasMaxHeapMB()                    | Check if the value with same name is available during the accompanying getXYZ() call
    +-----------------------------------+-----------------------------------------------------------------------------------
    | hasNumberOfRequests()                | Check if the value with same name is available during the accompanying getXYZ() call
    +-----------------------------------+-----------------------------------------------------------------------------------
    | hasTotalNumberOfRequests()        | Check if the value with same name is available during the accompanying getXYZ() call
    +-----------------------------------+-----------------------------------------------------------------------------------
    | hasUsedHeapMB()                    | Check if the value with same name is available during the accompanying getXYZ() call
    +-----------------------------------+-----------------------------------------------------------------------------------
    | obtainServerLoadPB()                | Returns the low-level Protobuf version of the current server load instance
    +-----------------------------------+-----------------------------------------------------------------------------------
    | toString()                        | Converts the state of the instance with all above metrics into a string for logging
    +-----------------------------------+-----------------------------------------------------------------------------------

    
對於 region 的負載,有一個專用的 RegionLoad 類,下面的表格列出其提供的信息:

    Overview of the information provided by the RegionLoad class
    +-------------------------------+---------------------------------------------------------------------------------------
    | Method                        | Description
    +-------------------------------+---------------------------------------------------------------------------------------
    | getCompleteSequenceId()        | Returns the last completed sequence ID for the region, used in conjunction with the MVCC
    +-------------------------------+---------------------------------------------------------------------------------------
    | getCurrentCompactedKVs()        | The currently compacted cells for this region, while a compaction is running
    +-------------------------------+---------------------------------------------------------------------------------------
    | getDataLocality()                | A ratio from 0 to 1 (0% to 100%) expressing the locality of store files to the region
    |                                | server process
    +-------------------------------+---------------------------------------------------------------------------------------
    | getMemStoreSizeMB()            | The heap size in megabytes as used by the MemStore of the current region
    +-------------------------------+---------------------------------------------------------------------------------------
    | getName()                        | The region name in its raw, byte[] byte array form
    +-------------------------------+---------------------------------------------------------------------------------------
    | getNameAsString()                | Converts the raw region name into a String for convenience
    +-------------------------------+---------------------------------------------------------------------------------------
    | getReadRequestsCount()        | The number of read requests for this region, since it was deployed to the region server.
    |                                | This counter is not reset.
    +-------------------------------+---------------------------------------------------------------------------------------
    | getRequestsCount()            | The number of requests for the current region
    +-------------------------------+---------------------------------------------------------------------------------------
    | getRootIndexSizeKB()            | The sum of all root index details help in memory for this region, in kilobytes
    +-------------------------------+---------------------------------------------------------------------------------------
    | getStorefileIndexSizeMB()        | The size of the indexes for all store files, in megabytes, for this region
    +-------------------------------+---------------------------------------------------------------------------------------
    | getStorefiles()                | The number of store files, across all stores of this region
    +-------------------------------+---------------------------------------------------------------------------------------
    | getStorefileSizeMB()            | The size in megabytes of the store files for this region
    +-------------------------------+---------------------------------------------------------------------------------------
    | getStores()                    | The number of stores in this region
    +-------------------------------+---------------------------------------------------------------------------------------
    | getStoreUncompressedSizeMB()    | The size of all stores in megabyte, before compression
    +-------------------------------+---------------------------------------------------------------------------------------
    | getTotalCompactingKVs()        | The count of all cells being compacted within this region
    +-------------------------------+---------------------------------------------------------------------------------------
    | getTotalStaticBloomSizeKB()    | The size of all Bloom filter data in kilobytes
    +-------------------------------+---------------------------------------------------------------------------------------
    | getTotalStaticIndexSizeKB()    | The size of all index data in kilobytes
    +-------------------------------+---------------------------------------------------------------------------------------
    | getWriteRequestsCount()        | The number of write requests for this region, since it was deployed to the region server.
    |                                | This counter is not reset
    +-------------------------------+---------------------------------------------------------------------------------------
    | toString()                    | Converts the state of the instance with all above metrics into a string for logging etc
    +-------------------------------+---------------------------------------------------------------------------------------


示例: Example reporting the status of a cluster

    //Get the cluster status
    ClusterStatus status = admin.getClusterStatus();

    System.out.println("Cluster Status:\n--------------");
    System.out.println("HBase Version: " + status.getHBaseVersion());
    System.out.println("Version: " + status.getVersion());
    System.out.println("Cluster ID: " + status.getClusterId());
    System.out.println("Master: " + status.getMaster());
    System.out.println("No. Backup Masters: " + status.getBackupMastersSize());
    System.out.println("Backup Masters: " + status.getBackupMasters());
    System.out.println("No. Live Servers: " + status.getServersSize());
    System.out.println("Servers: " + status.getServers());
    System.out.println("No. Dead Servers: " + status.getDeadServers());
    System.out.println("Dead Servers: " + status.getDeadServerNames());
    System.out.println("No. Regions: " + status.getRegionsCount());
    System.out.println("Regions in Transition: " +
    status.getRegionsInTransition());
    System.out.println("No. Requests: " + status.getRequestsCount());
    System.out.println("Avg Load: " + status.getAverageLoad());
    System.out.println("Balancer On: " + status.getBalancerOn());
    System.out.println("Is Balancer On: " + status.isBalancerOn());
    System.out.println("Master Coprocessors: " + Arrays.asList(status.getMasterCoprocessors()));

    System.out.println("\nServer Info:\n--------------");
    // Iterate over the included server instances
    for (ServerName server : status.getServers()) {
        System.out.println("Hostname: " + server.getHostname());
        System.out.println("Host and Port: " + server.getHostAndPort());
        System.out.println("Server Name: " + server.getServerName());
        System.out.println("RPC Port: " + server.getPort());
        System.out.println("Start Code: " + server.getStartcode());

        //Retrieve the load details for the current server
        ServerLoad load = status.getLoad(server);

        System.out.println("\nServer Load:\n--------------");
        System.out.println("Info Port: " + load.getInfoServerPort());
        System.out.println("Load: " + load.getLoad());
        System.out.println("Max Heap (MB): " + load.getMaxHeapMB());
        System.out.println("Used Heap (MB): " + load.getUsedHeapMB());
        System.out.println("Memstore Size (MB): " + load.getMemstoreSizeInMB());
        System.out.println("No. Regions: " + load.getNumberOfRegions());
        System.out.println("No. Requests: " + load.getNumberOfRequests());
        System.out.println("Total No. Requests: " + load.getTotalNumberOfRequests());
        System.out.println("No. Requests per Sec: " + load.getRequestsPerSecond());
        System.out.println("No. Read Requests: " + load.getReadRequestsCount());
        System.out.println("No. Write Requests: " + load.getWriteRequestsCount());
        System.out.println("No. Stores: " + load.getStores());
        System.out.println("Store Size Uncompressed (MB): " + load.getStoreUncompressedSizeMB());
        System.out.println("No. Storefiles: " + load.getStorefiles());
        System.out.println("Storefile Size (MB): " + load.getStorefileSizeInMB());
        System.out.println("Storefile Index Size (MB): " + load.getStorefileIndexSizeInMB());
        System.out.println("Root Index Size: " + load.getRootIndexSizeKB());
        System.out.println("Total Bloom Size: " + load.getTotalStaticBloomSizeKB());
        System.out.println("Total Index Size: " + load.getTotalStaticIndexSizeKB());
        System.out.println("Current Compacted Cells: " + load.getCurrentCompactedKVs());
        System.out.println("Total Compacting Cells: " + load.getTotalCompactingKVs());
        System.out.println("Coprocessors1: " + Arrays.asList(load.getRegionServerCoprocessors()));
        System.out.println("Coprocessors2: " + Arrays.asList(load.getRsCoprocessors()));
        System.out.println("Replication Load Sink: " + load.getReplicationLoadSink());
        System.out.println("Replication Load Source: " + load.getReplicationLoadSourceList());

        System.out.println("\nRegion Load:\n--------------");

        //Iterate over the region details of the current server
        for (Map.Entry<byte[], RegionLoad> entry :
        load.getRegionsLoad().entrySet()) {
            System.out.println("Region: " + Bytes.toStringBinary(entry.getKey()));
            
            //Get the load details for the current region.
            RegionLoad regionLoad = entry.getValue();
            
            System.out.println("Name: " + Bytes.toStringBinary(regionLoad.getName()));
            System.out.println("Name (as String): " + regionLoad.getNameAsString());
            System.out.println("No. Requests: " + regionLoad.getRequestsCount());
            System.out.println("No. Read Requests: " + regionLoad.getReadRequestsCount());
            System.out.println("No. Write Requests: " + regionLoad.getWriteRequestsCount());
            System.out.println("No. Stores: " + regionLoad.getStores());
            System.out.println("No. Storefiles: " + regionLoad.getStorefiles());
            System.out.println("Data Locality: " + regionLoad.getDataLocality());
            System.out.println("Storefile Size (MB): " + regionLoad.getStorefileSizeMB());
            System.out.println("Storefile Index Size (MB): " + regionLoad.getStorefileIndexSizeMB());
            System.out.println("Memstore Size (MB): " + regionLoad.getMemStoreSizeMB());
            System.out.println("Root Index Size: " + regionLoad.getRootIndexSizeKB());
            System.out.println("Total Bloom Size: " + regionLoad.getTotalStaticBloomSizeKB());
            System.out.println("Total Index Size: " + regionLoad.getTotalStaticIndexSizeKB());
            System.out.println("Current Compacted Cells: " + regionLoad.getCurrentCompactedKVs());
            System.out.println("Total Compacting Cells: " + regionLoad.getTotalCompactingKVs());
            System.out.println();
        }
    }

在獨立模式下運行,輸出結果類似於:

    Cluster Status:
    --------------
    HBase Version: 1.0.0
    Version: 2
    Cluster ID: 25ba54eb-09da-4698-88b5-5acdfecf0005
    Master: srv1.foobar.com,63911,1428996031794
    No. Backup Masters: 0
    Backup Masters: []
    No. Live Servers: 1
    Servers: [srv1.foobar.com,63915,1428996033410]
    No. Dead Servers: 2
    Dead Servers: [srv1.foobar.com,62938,1428669753889, srv1.foobar.com,60813,1428991052036]
    No. Regions: 7
    Regions in Transition: {}
    No. Requests: 56047
    Avg Load: 7.0
    Balancer On: true
    Is Balancer On: true
    Master Coprocessors: [MasterObserverExample]
    Server Info:
    --------------
    Hostname: srv1.foobar.com
    Host and Port: srv1.foobar.com:63915
    Server Name: srv1.foobar.com,63915,1428996033410
    RPC Port: 63915
    Start Code: 1428996033410
    Server Load:
    --------------
    Info Port: 63919
    Load: 7
    Max Heap (MB): 12179
    Used Heap (MB): 1819
    Memstore Size (MB): 651
    No. Regions: 7
    No. Requests: 56047
    Total No. Requests: 14334506
    No. Requests per Sec: 56047.0
    No. Read Requests: 2325
    No. Write Requests: 1239824
    No. Stores: 7
    Store Size Uncompressed (MB): 491
    No. Storefiles: 7
    Storefile Size (MB): 492
    Storefile Index Size (MB): 0
    Root Index Size: 645
    Total Bloom Size: 644
    Total Index Size: 389
    Current Compacted Cells: 51
    Total Compacting Cells: 51
    Coprocessors1: []
    Coprocessors2: []
    Replication Load Sink: org.apache.hadoop.hbase.replication.ReplicationLoadSink@582a4aa3
    Replication Load Source: []
    Region Load:
    --------------
    Region: TestTable,,1429009449882.3696e9469bb5a83bd9d7d67f7db65843.
    Name: TestTable,,1429009449882.3696e9469bb5a83bd9d7d67f7db65843.
    Name (as String): TestTable,,
    1429009449882.3696e9469bb5a83bd9d7d67f7db65843.
    No. Requests: 248324
    No. Read Requests: 0
    No. Write Requests: 248324
    No. Stores: 1
    No. Storefiles: 1
    Data Locality: 1.0
    Storefile Size (MB): 89
    Storefile Index Size (MB): 0
    Memstore Size (MB): 151
    Root Index Size: 116
    Total Bloom Size: 128
    Total Index Size: 70
    Current Compacted Cells: 0
    Total Compacting Cells: 0
    Region: TestTable,00000000000000000000209715,1429009449882.4be129aa6c8e3e00010f0a5824294eda.
    Name: TestTable,00000000000000000000209715,1429009449882.4be129aa6c8e3e00010f0a5824294eda.
    Name (as String): TestTable,
    00000000000000000000209715,1429009449882.4be129aa6c8e3e00010f0a5824294eda.
    No. Requests: 248048
    No. Read Requests: 0
    No. Write Requests: 248048
    No. Stores: 1
    No. Storefiles: 1
    Data Locality: 1.0
    Storefile Size (MB): 101
    Storefile Index Size (MB): 0
    Memstore Size (MB): 125
    Root Index Size: 132
    Total Bloom Size: 128
    Total Index Size: 80
    Current Compacted Cells: 0
    Total Compacting Cells: 0
    Region: TestTable,00000000000000000000419430,1429009449882.08acdaa21909f0085d64c1928afbf144.
    Name: TestTable,00000000000000000000419430,1429009449882.08acdaa21909f0085d64c1928afbf144.
    Name (as String): TestTable,
    00000000000000000000419430,1429009449882.08acdaa21909f0085d64c1928afbf144.
    No. Requests: 247868
    No. Read Requests: 0
    No. Write Requests: 247868
    No. Stores: 1
    No. Storefiles: 1
    Data Locality: 1.0
    Storefile Size (MB): 101
    Storefile Index Size (MB): 0
    Memstore Size (MB): 125
    Root Index Size: 133
    Total Bloom Size: 128
    Total Index Size: 80
    Current Compacted Cells: 0
    Total Compacting Cells: 0
    Region: TestTable,00000000000000000000629145,1429009449882.aaa91cddbfe2ed65bb35620f034f0c66.
    Name: TestTable,00000000000000000000629145,1429009449882.aaa91cddbfe2ed65bb35620f034f0c66.
    Name (as String): TestTable,
    00000000000000000000629145,1429009449882.aaa91cddbfe2ed65bb35620f034f0c66.
    No. Requests: 247971
    No. Read Requests: 0
    No. Write Requests: 247971
    No. Stores: 1
    No. Storefiles: 1
    Data Locality: 1.0
    Storefile Size (MB): 88
    Storefile Index Size (MB): 0
    Memstore Size (MB): 151
    Root Index Size: 116
    Total Bloom Size: 128
    Total Index Size: 70
    Current Compacted Cells: 0
    Total Compacting Cells: 0
    Region: TestTable,00000000000000000000838860,1429009449882.5a4243a8d734836f4818f115370fc089.
    Name: TestTable,00000000000000000000838860,1429009449882.5a4243a8d734836f4818f115370fc089.
    Name (as String): TestTable,
    00000000000000000000838860,1429009449882.5a4243a8d734836f4818f115370fc089.
    No. Requests: 247453
    No. Read Requests: 0
    No. Write Requests: 247453
    No. Stores: 1
    No. Storefiles: 1
    Data Locality: 1.0
    Storefile Size (MB): 113
    Storefile Index Size (MB): 0
    Memstore Size (MB): 99
    Root Index Size: 148
    Total Bloom Size: 132
    Total Index Size: 89
    Current Compacted Cells: 0
    Total Compacting Cells: 0
    Region: hbase:meta,,1
    Name: hbase:meta,,1
    Name (as String): hbase:meta,,1
    No. Requests: 2481
    No. Read Requests: 2321
    No. Write Requests: 160
    No. Stores: 1
    No. Storefiles: 1
    Data Locality: 1.0
    Storefile Size (MB): 0
    Storefile Index Size (MB): 0
    Memstore Size (MB): 0
    Root Index Size: 0
    Total Bloom Size: 0
    Total Index Size: 0
    Current Compacted Cells: 51
    Total Compacting Cells: 51
    Region: hbase:namespace,,
    1428669937904.0cfcd0834931f1aa683c765206e8fc0a.
    Name: hbase:namespace,,
    1428669937904.0cfcd0834931f1aa683c765206e8fc0a.
    Name (as String): hbase:namespace,,1428669937904.0cfcd0834931f1aa683c765206e8fc0a.
    No. Requests: 4
    No. Read Requests: 4
    No. Write Requests: 0
    No. Stores: 1
    No. Storefiles: 1
    Data Locality: 1.0
    Storefile Size (MB): 0
    Storefile Index Size (MB): 0
    Memstore Size (MB): 0
    Root Index Size: 0
    Total Bloom Size: 0
    Total Index Size: 0
    Current Compacted Cells: 0
    Total Compacting Cells: 0

    
*
*
*

3. 複製管理 (ReplicationAdmin)
-----------------------------------------------------------------------------------------------------------------------------------------
HBase 爲所有複製相關的操作(all replication purposes) 提供了一個單獨的管理 API。爲了說明清楚,這裏將其稱爲集羣間複製(cluster-to-cluster
replication), 而不是之前提到過的分區複製(region replica)

ReplicationAdmin 類提供了一個構造器,可以使用該構造器創建一個到另外一個集羣的連接,遠程集羣的信息由提供的配置實例提供:

    ReplicationAdmin(Configuration conf) throws IOException

一旦創建了實例,就可以使用如下方法建立當前集羣和遠程集羣間的複製:

    void addPeer(String id, String clusterKey) throws ReplicationException
    void addPeer(String id, String clusterKey, String tableCFs)
    void addPeer(String id, ReplicationPeerConfig peerConfig, Map<TableName, ? extends Collection<String>> tableCfs)
        throws ReplicationException
    void removePeer(String id) throws ReplicationException
    void enablePeer(String id) throws ReplicationException
    void disablePeer(String id) throws ReplicationException
    boolean getPeerState(String id) throws ReplicationException

一個夥伴(peer) 就是一個與當前集羣關聯的遠程集羣。它通過一個唯一性 ID 引用和 clusterKey 引用,clusterKey 由如下 peer 的配置組成:

    <hbase.zookeeper.quorum>:<hbase.zookeeper.property.clientPort>:<zookeeper.znode.parent>

例如:
    zk1.foo.com,zk2.foo.com,zk3.foo.com:2181:/hbase
    
遠程集羣 ZooKeeper 集合體(ZooKeeper ensemble) 有三部主機,用於偵聽客戶端的端口號,以及 HBase 存儲其數據的根路徑。這意爲着當前集羣可與列出
的遠程服務器通信。

peer 可與添加或移除,因此集羣間的複製是動態可配置的。一旦關係建立,實際的複製就可以啓用,或者禁用,不需要移除 peer 的信息就可以這樣做。
enablePeer() 方法啓動複製進程,而 disablePeer() 會停止對指定名稱 peer 的複製。可用於檢查當前的狀態,即對指定名稱的 peer 的複製是活動的還是
禁止的。

一旦集羣和它的 peer 建立了複製關係,就可以通過多種方法查詢信息:

    int getPeersCount()
    Map<String, String> listPeers()
    Map<String, ReplicationPeerConfig> listPeerConfigs()
    ReplicationPeerConfig getPeerConfig(String id) throws ReplicationException
    List<HashMap<String, String>> listReplicated() throws IOException
    
實踐中,將所有列族複製到所有 peer 是沒必要的,下列方法定義每個 peer, 每個列族(column family) 的關係。

    String getPeerTableCFs(String id) throws ReplicationException
    void setPeerTableCFs(String id, String tableCFs) throws ReplicationException
    void setPeerTableCFs(String id, Map<TableName, ? extends Collection<String>> tableCfs)
    void appendPeerTableCFs(String id, String tableCfs) throws ReplicationException
    void appendPeerTableCFs(String id, Map<TableName, ? extends Collection<String>> tableCfs)
    void removePeerTableCFs(String id, String tableCf) throws ReplicationException
    void removePeerTableCFs(String id, Map<TableName, ? extends Collection<String>> tableCfs)
    static Map<TableName, List<String>> parseTableCFsFromConfig(String tableCFsConfig)

對一個給定 peer ID, 可以設置和獲取複製的列族列表,也可以追加到列表中。

最後,在完成複製相關的管理 API 操作時,應該關閉該實例以釋放其佔用的資源:
    
    
    void close() throws IOException

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章