表格存储的 SDK 提供了 CreateTable、ListTable、DeleteTable、UpdateTable 和 DescribeTable 等表级别的操作接口。

创建表(CreateTable)

表格存储建表时需要指定表的结构信息(TableMeta)和配置信息(TableOptions),也可指定表的预留读/写吞吐量(ReservedThroughput)。

建表后服务端需要将表的分片加载到某个节点上,因此需要等待几秒钟才能对表进行读写,否则会抛出异常。

  • TableMeta

    TableMeta 包含表名和表的主键定义。

    • TableName:表名
    • List :表的主键定义
      • 表的主键可包含多个主键列。主键列是有顺序的,与用户添加的顺序相同。比如 PRIMARY KEY (A, B, C) 与 PRIMARY KEY (A, C, B) 是不同的两个主键结构。表格存储会按照整个主键的大小对行进行排序,具体请参见表格存储数据模型和查询操作
      • 第一列主键列的值作为分片键。分片键相同的数据肯定会在同一个分片内,所以相同分片键下最好不要超过 10 G 以上数据,否则会导致单分片过大(无法分裂)。另一方面,数据和读/写访问最好在不同的分片键上均匀分布,有利于负载均衡,在设计表结构时需要注意这一点。
      • 用户建表时只需要定义表的主键,属性列不需要定义。每行的数据列都可以不同,属性列的列名在写入时指定。因此,表格存储非常适合存储半结构化的数据,在业务发展过程中可以动态添加新的数据列。
  • TableOptions

    TableOptions 包含表的 TTL、MaxVersions 和 MaxTimeDeviation 配置。

    • TTL:TimeToLive,数据存活时间,单位秒。
      • 表格存储的新版 API 支持数据自动过期。如果期望永不过期,TTL 可设置为 -1。
      • 数据是否过期是根据数据的时间戳当前时间表的 TTL三者进行判断的。当当前时间减去数据的时间戳大于表的 TTL时,数据会过期并被表格存储服务器端清理。关于数据的时间戳的更多信息,请参见数据模型概念。
      • 当设置 TTL 后,由于判断过期涉及数据的时间戳,如果用户指定时间戳写入,且指定的时间戳严重偏离当前时间,那么可能导致未预料的数据过期行为。比如指定的数据时间戳很小,可能导致数据一写入就被过期回收了。当指定的数据时间戳很大时,又可能导致期望过期的数据过期不掉。因此在使用 TTL 功能时需要注意写入时是否指定了时间戳,以及指定的时间戳是否合理。
    • MaxVersions:每个属性列保留的最大版本数。

      表格存储的新版 API 支持多版本的数据模型。MaxVersions 即用来指定每个属性列最多保存多少个版本的数据,如果写入的版本数超过 MaxVersions,服务端只会保留版本号最大的 MaxVersions 个版本。

    • MaxTimeDeviation:指定版本写入数据时所指定的版本与系统当前时间偏差允许的最大值,单位为秒。

      表格存储支持多版本,默认情况下系统会为新写入的数据生成一个版本号,是写入时间的毫秒单位时间戳,数据自动过期功能需要根据这个时间戳判断数据是否过期。另一方面,用户可以指定写入数据的时间戳,因此如果用户写入的时间戳非常小,与当前时间偏差已经超过了表上设置的 TTL 时间,写入的数据会立即过期。出于保护的目的,在表上增加了 MaxTimeDeviation 设置,限制写入数据的时间戳与系统当前时间的偏差,该值的单位为秒,可在建表时指定,也可通过 UpdateTable 接口修改。

  • ReservedThroughtput

    表的预留读/写吞吐量配置,与计费相关。当预留读/写吞吐量大于 0 时,会按照预留量和持续时间进行计费,超出预留的部分进行按量计费。默认预留读写吞吐量为 0,即完全按量计费,如果要设置为大于 0 的值,请仔细阅读表格存储的计费相关文档,以免产生未期望的费用。

    容量型实例的预留读/写吞吐量只能设置为 0,不允许预留。

    示例

    private static void createTable(SyncClient client) {
            TableMeta tableMeta = new TableMeta(TABLE_NAME);
            tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema(PRIMARY_KEY_NAME, PrimaryKeyType.STRING));
            // 数据的过期时间, 单位秒, -1代表永不过期. 假如设置过期时间为一年, 即为 365 * 24 * 3600.
            int timeToLive = -1; 
            // 保存的最大版本数, 设置为3即代表每列上最多保存3个最新的版本.
            int maxVersions = 3; 
            TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);
            CreateTableRequestEx request = new CreateTableRequestEx(tableMeta, tableOptions);
            // 设置读写预留值,容量型实例只能设置为0,高性能实例可以设置为非零值
             request.setReservedThroughput(new ReservedThroughput(new CapacityUnit(0, 0)));
            client.createTable(request);
        }

列出表名称(ListTable)

获取当前实例下已创建的所有表的表名。

示例

private static void listTable(SyncClient client) {
    ListTableResponse response = client.listTable();
    System.out.println("表的列表如下:");
    for (String tableName : response.getTableNames()) {
        System.out.println(tableName);
    }
}

更新表(UpdateTable)

表格存储支持更新表的预留读/写吞吐量(ReservedThroughput)和配置信息(TableOptions)。

关于 ReservedThroughput 和 TableOptions,在本章开始的创建表部分已经有过介绍。ReservedThroughput 的调整有时间间隔限制,目前为 1 分钟。

示例

更新表的TTL和最大版本数。

private static void updateTable(SyncClient client) {
    int timeToLive = -1;
    int maxVersions = 5; //更新最大版本数为5.
    TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);
    UpdateTableRequest request = new UpdateTableRequest(TABLE_NAME);
    request.setTableOptionsForUpdate(tableOptions);
    client.updateTable(request);
}

查询表描述信息(DescribeTable)

DescribeTable 接口可以查询表的结构信息(TableMeta)、配置信息(TableOptions)和预留读/写吞吐量的情况(ReservedThroughputDetails)。TableMeta 和 TableOptions 在建表一节已经有过介绍,ReservedThroughputDetails 除了包含表的预留吞吐量的值外,还包括最近一次上调或者下调的时间。

示例

获取表的描述信息。

private static void describeTable(SyncClient client) {
    DescribeTableRequest request = new DescribeTableRequest(TABLE_NAME);
    DescribeTableResponse response = client.describeTable(request);
    TableMeta tableMeta = response.getTableMeta();
    System.out.println("表的名称:" + tableMeta.getTableName());
    System.out.println("表的主键:");
    for (PrimaryKeySchema primaryKeySchema : tableMeta.getPrimaryKeyList()) {
        System.out.println(primaryKeySchema);
    }
    TableOptions tableOptions = response.getTableOptions();
    System.out.println("表的TTL:" + tableOptions.getTimeToLive());
    System.out.println("表的MaxVersions:" + tableOptions.getMaxVersions());
    ReservedThroughputDetails reservedThroughputDetails = response.getReservedThroughputDetails();
    System.out.println("表的预留读吞吐量:"
            + reservedThroughputDetails.getCapacityUnit().getReadCapacityUnit());
    System.out.println("表的预留写吞吐量:"
            + reservedThroughputDetails.getCapacityUnit().getWriteCapacityUnit());
}

删除表(DeleteTable)

删除本实例下指定的表。

示例

删除表。

private static void deleteTable(SyncClient client) {
    DeleteTableRequest request = new DeleteTableRequest(TABLE_NAME);
    client.deleteTable(request);
}

指定大小计算分片(ComputeSplitsBySize)

将全表数据逻辑上划分成接近指定大小的若干分片,返回这些分片之间的分割点以及分片所在机器的提示。一般用于计算引擎规划并发度等执行计划。

示例

指定大小计算分片

private static void describeTable(SyncClient client) {
    // 以200MB划分分片
    ComputeSplitsBySizeRequest request = new ComputeSplitsBySizeRequest(TABLE_NAME, 2);
    ComputeSplitsBySizeResponse response = client.computeSplitsBySize(computeSplitsBySizeRequest);
    System.out.println("ConsumedCapacity=" + response.getConsumedCapacity().jsonize());
    System.out.println("PrimaryKeySchema=" + response.getPrimaryKeySchema());
    System.out.println("RequestId=" + response.getRequestId());
    System.out.println("TraceId=" + response.getTraceId());
    List<Split> splits = response.getSplits();
    System.out.println("splits.size=" + splits.size());
    Iterator<Split> iterator = splits.iterator();
    while (iterator.hasNext()) {
        Split split = iterator.next();
        System.out.println("split.getLocation()=" + split.getLocation());
        // split.getLowerBound()和split.getUpperBound()可以直接灌进RangeRowQueryCriteria交给getRange()或createRangeIterator()
        System.out.println("split.getLowerBound()=" + split.getLowerBound().jsonize());
        System.out.println("split.getLowerBound()=" + split.getUpperBound().jsonize());
    }
}