如何使用DatastoreIO和Dataflow批量删除实体上的数百万

时间:2016-08-10 00:47:07

标签: google-cloud-dataflow

我尝试使用Dataflow删除数百万个数据存储区实体,速度非常慢(5个实体/秒)。我希望你能向我解释我应该遵循的模式,以便扩大到合理的速度。只是添加更多工人没有帮助。

数据存储管理控制台能够删除特定种类的所有实体,但它失败很多,需要一周或更长时间才能删除4000万个实体。数据流应该能够帮助我删除数百万只与某些查询参数匹配的实体。

我猜测应该采用某种类型的批处理策略(例如,我在其中创建了一个包含1000个删除的突变),但对我来说这并不明显。 DatastoreIO一次只给我一个实体。指针将不胜感激。

以下是我目前的慢速解决方案。

Pipeline p = Pipeline.create(options);
DatastoreIO.Source source = DatastoreIO.source()
    .withDataset(options.getDataset())
    .withQuery(getInstrumentQuery(options))
    .withNamespace(options.getNamespace());
p.apply("ReadLeafDataFromDatastore", Read.from(source))
 .apply("DeleteRecords", ParDo.of(new DeleteInstrument(options.getDataset())));
p.run();

static class DeleteInstrument extends DoFn<Entity, Integer> {
 String dataset;
  DeleteInstrument(String dataset) {
    this.dataset = dataset;
  }
  @Override
  public void processElement(ProcessContext c) {
    DatastoreV1.Mutation.Builder mutation = DatastoreV1.Mutation.newBuilder();
    mutation.addDelete(c.element().getKey());
    final DatastoreV1.CommitRequest.Builder request = DatastoreV1.CommitRequest.newBuilder();
    request.setMutation(mutation);
    request.setMode(DatastoreV1.CommitRequest.Mode.NON_TRANSACTIONAL);
    try {
      DatastoreOptions.Builder dbo = new DatastoreOptions.Builder();
      dbo.dataset(dataset);
      dbo.credential(getCredential());
      Datastore db = DatastoreFactory.get().create(dbo.build());
      db.commit(request.build());
      c.output(1);
      count++;
      if(count%100 == 0) {
        LOG.info(count+"");
      }
    } catch (Exception e) {
      c.output(0);
      e.printStackTrace();
    }
  }
}

1 个答案:

答案 0 :(得分:4)

使用当前版本的DatastoreIO没有直接删除实体的方法。在下一个Dataflow版本中,将弃用此版本的DatastoreIO以支持新版本(v1beta3)。我们认为提供删除实用程序(通过示例或PTransform)有一个很好的用例,但仍在进行中。

现在,您可以批量删除,而不是一次删除一个:

  public static class DeleteEntityFn extends DoFn<Entity, Void> {
    // Datastore max batch limit
    private static final int DATASTORE_BATCH_UPDATE_LIMIT = 500;
    private Datastore db;
    private List<Key> keyList = new ArrayList<>();

    @Override
    public void startBundle(Context c) throws Exception {
      // Initialize Datastore Client 
      // db = ...  
    }

    @Override
    public void processElement(ProcessContext c) throws Exception {
      keyList.add(c.element().getKey());
      if (keyList.size() >= DATASTORE_BATCH_UPDATE_LIMIT) {
        flush();
      }
    }

    @Override
    public void finishBundle(Context c) throws Exception {
      if (keyList.size() > 0) {
        flush();
      }
    }

    private void flush() throws Exception {
      // Make one delete request instead of one for each element.
      CommitRequest request =
          CommitRequest.newBuilder()
              .setMode(CommitRequest.Mode.NON_TRANSACTIONAL)
              .setMutation(Mutation.newBuilder().addAllDelete(keyList).build())
              .build();
      db.commit(request);
      keyList.clear();
    }
  }