Bigtable 和 Dataflow:数据库监控技术(HBase Java 客户端)

1. 简介

在此 Codelab 中,您将使用 Cloud Bigtable 的监控工具,通过使用 Cloud DataflowJava HBase 客户端写入和读取数据,来创作各种艺术作品。

您将了解如何

  • 使用 Cloud Dataflow 将大量数据加载到 Bigtable 中
  • 在提取数据时监控 Bigtable 实例和表
  • 使用 Dataflow 作业查询 Bigtable
  • 探索 Key Visualizer 工具,该工具可用于根据您的架构设计找出热点
  • 使用 Key Visualizer 打造艺术作品

d098cc81f78f02eb.png

您如何评价自己使用 Cloud Bigtable 的体验?

新手水平 中等水平 熟练水平

您打算如何使用本教程?

<ph type="x-smartling-placeholder"></ph> 仅通读 阅读并完成练习

2. 创建 Bigtable 数据库

Cloud Bigtable 是 Google 面向大数据领域的 NoSQL 数据库服务。它也是为 Google 搜索、Google Analytics、Google 地图和 Gmail 等众多核心 Google 服务提供支撑的数据库。它非常适合运行大型分析工作负载和构建低延迟应用。查看 Cloud Bigtable 简介 Codelab 以获取深入介绍。

创建项目

首先,创建一个新项目。使用内置的 Cloud Shell(点击“激活 Cloud Shell”即可打开)按钮。

a74d156ca7862b28.png

设置以下环境变量,以便更轻松地复制和粘贴 Codelab 命令:

BIGTABLE_PROJECT=$GOOGLE_CLOUD_PROJECT
INSTANCE_ID="keyviz-art-instance"
CLUSTER_ID="keyviz-art-cluster"
TABLE_ID="art"
CLUSTER_NUM_NODES=1
CLUSTER_ZONE="us-central1-c" # You can choose a zone closer to you

Cloud Shell 附带您将在此 Codelab 中使用的工具,即 gcloud 命令行工具cbt 命令行界面Maven

运行此命令来启用 Cloud Bigtable API。

gcloud services enable bigtable.googleapis.com bigtableadmin.googleapis.com

通过运行以下命令来创建实例:

gcloud bigtable instances create $INSTANCE_ID \
    --cluster=$CLUSTER_ID \
    --cluster-zone=$CLUSTER_ZONE \
    --cluster-num-nodes=$CLUSTER_NUM_NODES \
    --display-name=$INSTANCE_ID

创建实例后,填充 cbt 配置文件,然后通过运行以下命令创建表和列族:

echo project = $GOOGLE_CLOUD_PROJECT > ~/.cbtrc
echo instance = $INSTANCE_ID >> ~/.cbtrc

cbt createtable $TABLE_ID
cbt createfamily $TABLE_ID cf

3. 学习:使用 Dataflow 向 Bigtable 写入数据

写作基础知识

向 Cloud Bigtable 写入数据时,您必须提供一个 CloudBigtableTableConfiguration 配置对象。此对象指定了表的项目 ID 和实例 ID,以及表本身的名称:

CloudBigtableTableConfiguration bigtableTableConfig =
    new CloudBigtableTableConfiguration.Builder()
        .withProjectId(PROJECT_ID)
        .withInstanceId(INSTANCE_ID)
        .withTableId(TABLE_ID)
        .build();

然后,流水线可以传递 HBase Mutation 对象,其中可能包括 Put 和 Delete。

p.apply(Create.of("hello", "world"))
    .apply(
        ParDo.of(
            new DoFn<String, Mutation>() {
              @ProcessElement
              public void processElement(@Element String rowkey, OutputReceiver<Mutation> out) {
                long timestamp = System.currentTimeMillis();
                Put row = new Put(Bytes.toBytes(rowkey));

                row.addColumn(...);
                out.output(row);
              }
            }))
    .apply(CloudBigtableIO.writeToTable(bigtableTableConfig));

LoadData Dataflow 作业

下一页将介绍如何运行 LoadData 作业,但我在这里只介绍流水线的重要部分。

要生成数据,您需要创建一个使用 GenerateSequence 类(类似于 for 循环)的流水线,以写入若干行(包含几兆字节的随机数据)。rowkey 将是填充和反转的序列号,因此 250 会变为 0000000052

LoadData.java

String numberFormat = "%0" + maxLength + "d";

p.apply(GenerateSequence.from(0).to(max))
    .apply(
        ParDo.of(
            new DoFn<Long, Mutation>() {
              @ProcessElement
              public void processElement(@Element Long rowkey, OutputReceiver<Mutation> out) {
                String paddedRowkey = String.format(numberFormat, rowkey);

                // Reverse the rowkey for more efficient writing
                String reversedRowkey = new StringBuilder(paddedRowkey).reverse().toString();
                Put row = new Put(Bytes.toBytes(reversedRowkey));

                // Generate random bytes
                byte[] b = new byte[(int) rowSize];
                new Random().nextBytes(b);

                long timestamp = System.currentTimeMillis();
                row.addColumn(Bytes.toBytes(COLUMN_FAMILY), Bytes.toBytes("C"), timestamp, b);
                out.output(row);
              }
            }))
    .apply(CloudBigtableIO.writeToTable(bigtableTableConfig));

4. 将数据生成到 Bigtable 中并监控流入

以下命令将运行一个 Dataflow 作业,该作业向表中生成 40GB 的数据,这足以让 Key Visualizer 激活它:

启用 Cloud Dataflow API

gcloud services enable dataflow.googleapis.com

从 GitHub 获取代码并切换到该目录

git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git
cd java-docs-samples/bigtable/beam/keyviz-art

生成数据(脚本大约需要 15 分钟)

mvn compile exec:java -Dexec.mainClass=keyviz.LoadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT"

监控导入

您可以在 Cloud Dataflow 界面中监控该作业。此外,您还可以通过 Cloud Bigtable 实例的监控界面查看其负载。

在 Dataflow 界面中,您可以看到作业图和各种作业指标,包括已处理的元素、当前 vCPU 和吞吐量。

9cecc290f5acea15.png

abb0561342dc6b60.png

Bigtable 提供标准监控工具,可用于监控实例级、集群级和表级读写操作、已使用的存储空间、错误率等。除此之外,Bigtable 还具有 Key Visualizer,它会根据行键细分您的使用情况,当至少生成 30GB 的数据后,我们就会使用该行键。

996f8589332dfc19

5. 学习:使用 Dataflow 从 Bigtable 读取数据

阅读基础知识

从 Cloud Bigtable 读取数据时,您必须提供一个 CloudBigtableTableScanConfiguration 配置对象。它与 CloudBigtableTableConfiguration 类似,但您可以指定要扫描和读取的行。

Scan scan = new Scan();
scan.setCacheBlocks(false);
scan.setFilter(new FirstKeyOnlyFilter());

CloudBigtableScanConfiguration config =
    new CloudBigtableScanConfiguration.Builder()
        .withProjectId(options.getBigtableProjectId())
        .withInstanceId(options.getBigtableInstanceId())
        .withTableId(options.getBigtableTableId())
        .withScan(scan)
        .build();

然后使用它来启动流水线:

p.apply(Read.from(CloudBigtableIO.read(config)))
    .apply(...

不过,如果您想在流水线中执行读取操作,可以将 CloudBigtableTableConfiguration 传递给扩展 AbstractCloudBigtableTableDoFndoFn

p.apply(GenerateSequence.from(0).to(10))
    .apply(ParDo.of(new ReadFromTableFn(bigtableTableConfig, options)));

然后,使用您的配置调用 super()getConnection(),以获取分布式连接。

public static class ReadFromTableFn extends AbstractCloudBigtableTableDoFn<Long, Void> {
    public ReadFromTableFn(CloudBigtableConfiguration config, ReadDataOptions readDataOptions) {
      super(config);
    }

    @ProcessElement
    public void processElement(PipelineOptions po) {
        Table table = getConnection().getTable(TableName.valueOf(options.getBigtableTableId()));
        ResultScanner imageData = table.getScanner(scan);
    }   
}

ReadData Dataflow 作业

在此 Codelab 中,您需要每秒从表中读取数据,因此您可以使用生成的序列启动流水线,该序列根据输入的 CSV 文件的时间触发多个读取范围。

要确定在指定时间内扫描哪些行范围,需要完成一些数学运算,但如果您想了解详情,可以点击文件名来查看源代码。

ReadData.java

p.apply(GenerateSequence.from(0).withRate(1, new Duration(1000)))
    .apply(ParDo.of(new ReadFromTableFn(bigtableTableConfig, options)));

ReadData.java

  public static class ReadFromTableFn extends AbstractCloudBigtableTableDoFn<Long, Void> {

    List<List<Float>> imageData = new ArrayList<>();
    String[] keys;

    public ReadFromTableFn(CloudBigtableConfiguration config, ReadDataOptions readDataOptions) {
      super(config);
      keys = new String[Math.toIntExact(getNumRows(readDataOptions))];
      downloadImageData(readDataOptions.getFilePath());
      generateRowkeys(getNumRows(readDataOptions));
    }

    @ProcessElement
    public void processElement(PipelineOptions po) {
      // Determine which column will be drawn based on runtime of job.
      long timestampDiff = System.currentTimeMillis() - START_TIME;
      long minutes = (timestampDiff / 1000) / 60;
      int timeOffsetIndex = Math.toIntExact(minutes / KEY_VIZ_WINDOW_MINUTES);

      ReadDataOptions options = po.as(ReadDataOptions.class);
      long count = 0;

      List<RowRange> ranges = getRangesForTimeIndex(timeOffsetIndex, getNumRows(options));
      if (ranges.size() == 0) {
        return;
      }

      try {
        // Scan with a filter that will only return the first key from each row. This filter is used
        // to more efficiently perform row count operations.
        Filter rangeFilters = new MultiRowRangeFilter(ranges);
        FilterList firstKeyFilterWithRanges = new FilterList(
            rangeFilters,
            new FirstKeyOnlyFilter(),
            new KeyOnlyFilter());
        Scan scan =
            new Scan()
                .addFamily(Bytes.toBytes(COLUMN_FAMILY))
                .setFilter(firstKeyFilterWithRanges);

        Table table = getConnection().getTable(TableName.valueOf(options.getBigtableTableId()));
        ResultScanner imageData = table.getScanner(scan);
      } catch (Exception e) {
        System.out.println("Error reading.");
        e.printStackTrace();
      }
    }

    /**
     * Download the image data as a grid of weights and store them in a 2D array.
     */
    private void downloadImageData(String artUrl) {
    ...
    }

    /**
     * Generates an array with the rowkeys that were loaded into the specified Bigtable. This is
     * used to create the correct intervals for scanning equal sections of rowkeys. Since Bigtable
     * sorts keys lexicographically if we just used standard intervals, each section would have
     * different sizes.
     */
    private void generateRowkeys(long maxInput) {
    ...
    }

    /**
     * Get the ranges to scan for the given time index.
     */
    private List<RowRange> getRangesForTimeIndex(@Element Integer timeOffsetIndex, long maxInput) {
    ...
    }
  }

6. 创作精彩作品

ad9c4c0b90626a3b.png

现在,您已了解如何将数据加载到 Bigtable 中并使用 Dataflow 从中读取数据,您可以运行最后一个命令了,该命令将在 8 小时内生成《蒙娜丽莎》的图片。

mvn compile exec:java -Dexec.mainClass=keyviz.ReadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT"

您可以使用包含现有图片的存储分区。您也可以使用此工具根据您自己的任何图片创建输入文件,然后将其上传到公开的 GCS 存储分区。

文件名根据 gs://keyviz-art/[painting]_[hours]h.txt 生成,示例:gs://keyviz-art/american_gothic_4h.txt

绘制选项:

  • american_gothic
  • mona_lisa
  • pearl_earring
  • persistence_of_memory
  • starry_night
  • sunday_afternoon
  • the_scream

小时选项:1、4、8、12、24、48、72、96、120、144

allUsers 授予角色 Storage Object Viewer,以公开您的 GCS 存储分区或文件。

ee089815364150d2.png

选择图片后,只需在以下命令中更改 --file-path 参数即可:

mvn compile exec:java -Dexec.mainClass=keyviz.ReadData \
"-Dexec.args=--bigtableProjectId=$BIGTABLE_PROJECT \
--bigtableInstanceId=$INSTANCE_ID --runner=dataflow \
--bigtableTableId=$TABLE_ID --project=$GOOGLE_CLOUD_PROJECT \
--filePath=gs://keyviz-art/american_gothic_4h.txt"

7. 稍后再查看

完整图像可能需要几个小时才能生效,但在 30 分钟后,您应该就会开始在 Key Visualizer 中看到活动。您可以使用以下参数:缩放、亮度和公制。您可以通过以下方法进行缩放:使用鼠标上的滚轮,或在 Key Visualizer 网格上拖动矩形。

亮度可以更改图片的缩放比例,如果您想深入观察非常热的区域,这会非常有用。

8e847f03df25572b

您还可以调整要显示的指标。有 OP、读取字节客户端、写入字节客户端等等。"读取字节客户端"似乎生成了平滑的图片,而“Ops”生成的图片中的线条越多,这在一些图片上看起来会非常棒。

33eb5dcf4e4be861

8. 完成

清理相关资源以避免产生费用

为避免系统因此 Codelab 中使用的资源向您的 Google Cloud Platform 账号收取费用,您应删除您的实例。

gcloud bigtable instances delete $INSTANCE_ID

所学内容

  • 使用 Dataflow 向 Bigtable 写入数据
  • 使用 Dataflow 从 Bigtable 读取数据(在流水线的起点,流水线的中间)
  • 使用 Dataflow 监控工具
  • 使用 Bigtable 监控工具(包括 Key Visualizer)

后续步骤