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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: HBase是一个分布式的、面向列的NoSQL数据库,它是建立在Hadoop之上的。HBase提供了Java API管理表,这些API可以用来创建、删除、修改和查询表。使用Java API可以方便地与HBase进行交互,实现数据的读写和管理。在使用Java API时,需要先创建一个HBaseConfiguration对象,然后通过该对象创建一个HBaseAdmin对象,通过HBaseAdmin对象可以进行表的管理操作。同时,还可以使用HBase的Put和Get对象来进行数据的读写操作。总之,使用Java API可以方便地管理HBase表,实现数据的高效存储和查询。 ### 回答2: HBase是一个高可扩展性、高可靠性的分布式列存储系统,常用于海量数据的存储与实时查询。在HBase中,表被分成若干区域(Region),每个Region包含一段rowkey范围内的数据。HBase使用Zookeeper协调Region Server和HMaster的进程启动和监管系统,其提供了简单的Java API进行操作和使用。 HBase中的Java API提供了一系列操作表的方法,主要包括创建表、删除表、获取表信息、插入数据、查询数据和删除数据等操作。 首先,创建表需要确定表名、列族和行键的设计。使用Java API时,可使用TableDescriptorBuilder来创建表的描述,其中需要指定表名和列族名。对于每个列族,需要指定数据是否压缩、存储类型等信息。 其次,对于已有表进行删除时,首先要停止对该表的所有操作,并将其进行禁用。使用Java API时,可使用Admin.disableTable()方法对表进行禁用,然后通过Admin.deleteTable()方法将表进行删除。 获取表信息可使用Admin.getDescriptor()方法获取表的描述信息,包括表名、列族和数据存储信息等。 对于插入数据,HBase中的数据是以KV(Key-Value)的形式存储,所以需要一个Put对象来承载需要存储的数据。使用Table.put()方法可以将数据存储到对应的表中。 查询数据可使用Scan或Get方法,其中Scan可针对整个表进行扫描,Get可获取指定的行键的数据。使用Scan和Get方法可获取批量数据和单条数据,具体使用时根据实际情况进行选择。 删除数据可使用Delete方法,可以删除指定行键的数据。使用Table.delete()方法可以实现对数据的删除操作。 总之,HBase的Java API提供了方便快捷的方式对HBase的表进行管理和操作,可以很好地满足海量数据的存储和实时查询需求。 ### 回答3: HBase是一个开源的NoSQL数据库,采用分布式存储的方式来存储数据,并且可以在百万级别的数据规模下保证数据的高可靠性、高可扩展性和高性能。HBase提供了Java API管理表,实现数据的增删改查等操作。 在使用HBase Java API管理表时,需要先连接到HBase集群。可以通过HBaseConfiguration类来创建一个Configuration实例,该实例包含了与Hadoop和HBase相关的配置信息,然后通过ConnectionFactory类的createConnection方法来创建一个Connection实例,即可连接到HBase集群。 对于表的管理HBase提供了Table类,通过该类的实例可以进行数据的增删改查操作。需要通过TableDescriptorBuilder构建表的描述信息,包括表名、列族等信息,然后通过Admin类的createTable方法来创建表。如果需要删除表,则可以使用Admin类的deleteTable方法来删除表。 对于数据的增删改查操作,需要先获取到Table类的实例,然后通过Put类、Delete类和Get类来进行数据的插入、删除和查询操作。对于Put类的实例,需要通过addColumn方法来定义要插入的列族和列,然后通过add方法来设置列的值;对于Delete类的实例,需要通过addColumn方法来定义要删除的列族和列;对于Get类的实例,则需要通过addColumn方法来定义要查询的列族和列,然后通过Result类的实例来获取查询结果。 在进行数据操作时,如果需要批量操作,可以使用Batch类的实例来进行批量操作。Batch类提供了一系列add方法用于添加Put、Delete和Increment对象,然后通过Table类的batch方法来批量提交操作。 总之,HBase Java API提供了一系列方便的方法来管理表和进行数据操作,可以满足大规模数据存储的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值