【问题标题】:Google Cloud BigTable : Update the column valueGoogle Cloud BigTable:更新列值
【发布时间】:2018-11-08 10:17:51
【问题描述】:

我有以下 BigTable 结构为例:

Table1 : column_family_1 : column_1 : value

这里的value 是一个数字。这是由数据流管理的,我想每次都更新值。

这个值可能是一个金额,我想在用户每次购买时更新它(以保持迄今为止的总支出),所以我在购买事件监听器数据流中执行以下操作(每当遇到购买事件时):

  • 发出 BigTable 请求以通过 id 获取值
  • 将新购买的金额添加到 BigTable 搜索响应中的金额
  • 发出Put 请求以更新值

虽然这种方法有一些网络延迟,但它似乎有效。失败的情况是,当数据流有多个工作人员时,用户进行了多次购买并且事件转到多个工作人员,例如:

  • Worker 1 获取事件 1,获取金额并将花费的金额添加到其中
  • Worker 2 获取事件 2,获取 金额并将已花费金额添加到其中
  • 两个工作人员都提出了Put 请求,但他们被覆盖了

为了防止这种情况,我正在尝试提出一个请求,该请求仅以纯文本形式显示,add 10 to the spent amount value。这是我们可以在数据流中做的事情吗?

【问题讨论】:

  • 我了解这种情况以及失败的时间/原因,但我不明白你是如何阻止它的。请您改写或详细说明这部分:“为了防止这种情况,我正在尝试提出一个请求,该请求仅以纯文本形式显示,add 10 to the spent amount value。这是我们可以在数据流中执行的操作吗?”
  • @RubénC。我试图通过 upsert 命令阻止它,它的 SQL 解释将类似于UPDATE table SET amount = amount + <new_deposit> WHERE person_id = <person_id>;所以,我不是读取它,添加值并将其写回,而是通过发出增量或更新命令来更新它。跨度>

标签: google-cloud-dataflow apache-beam bigtable google-cloud-bigtable


【解决方案1】:

Bigtable 能够处理Increment 值。您可以在protobuf documentation 中查看更多详细信息。

幂等性在理解 Bigtable 中的计数器方面起着重要作用。 在 Bigtable 中,Puts 通常是幂等的,这意味着您可以多次运行它们并始终得到相同的结果(a=2 无论您运行多少次都会产生相同的结果)。 Increments 不是幂等的,因为多次运行它们会产生不同的结果(a++a++a++a++a++ 的结果不同)。

瞬态故障可能会也可能不会执行Increment。客户端永远无法清楚 Increment 在这些瞬时错误期间是否成功。

由于这种幂等性,在 Dataflow 中构建此 Increment 功能很复杂。数据流有一个“捆绑包”的概念,它是一组作为工作单元的操作。这些捆绑包会因暂时性故障而重试(您可以阅读有关 Dataflow 暂时性故障重试here 的更多信息)。 Dataflow 将“捆绑”视为一个单元,错误 Cloud Bigtable 必须将“捆绑”中的每个单独项目视为不同的事务,因为 Cloud Bigtable 不支持多行事务。

鉴于“捆绑包”的预期行为不匹配,Cloud Bigtable 将不允许您通过 Dataflow 运行 Increments。

您拥有的选项应该得到比我在此处提供的更多的文档,但我可以提供一些高级别的选项:

  1. 对于您发现的任何新事件,始终使用Put,并总结读数上的值。您还可以编写另一个定期清理行的作业,方法是创建一个删除所有当前值的“事务”,并用总和写入一个新单元格

  2. 使用Cloud Functions 侦听 Pub/Sub 事件并执行 Increments。这是Cloud Bigtable example using Cloud Functions。您还可以执行Get,执行加法并使用您在帖子中描述的算法执行CheckAndMutate(如果我要选择此选项,我个人会选择CheckAndMutate 以保持一致性)。

  3. 使用AbstractCloudBigtableTableDoFn 编写您自己的DoFn 来执行Increments 或CheckAndMutate,但要了解这可能会导致数据完整性问题。

如果系统足够大,选项 #1 是您最强大的选项,但会以系统复杂性为代价。如果您不想要那种复杂性,那么选项#2 是您的下一个最佳选择(尽管我会选择CheckAndMutate)。如果您不关心数据完整性并且需要高吞吐量(例如“页数”或其他可以在一小部分时间出错的遥测数据),那么选项 #3 将是您的最佳选择。

【讨论】:

  • 感谢您的全面解释。就我们的用例而言,我们需要数据完整性和高吞吐量。根据第一个解决方案,如果我们不编写清理作业并在读取时执行sum 聚合,它是否会使聚合随着大小的增长而显着变慢?另一方面,我们可以在相同的行上使用Put,但追加新的单元格吗?
  • 另外,在查看了您的代码之后,您认为我应该继续在多个数据流工作线程中使用多个 ReadModifyWrite 请求(使用 apache 光束)吗?
  • 聚合确实随着规模的增长而变慢。如果您使用 Put,请确保设置时间戳,以免出现重复。多个 ReadModifyWrite 可能会导致正确性问题。如果您要使用 ReadModifyWrite,那么您可能需要使用 Cloud Functions。
  • 我们需要在这个数据流中做一个跨表事务(不仅仅是交叉row),我猜Bigtable不支持。在这种情况下,我们是否可以不编写自己的代码来处理这个问题,例如if(row doesn't exist in raw table){aggregate();}insertIntoRawTable();。这样,如果重试捆绑包并且重试插入将覆盖重复项,它将不会重试聚合。
  • 这将适用于密钥存在的第一批,但不适用于后续事件。
【解决方案2】:

另一种解决方案可能如下:

  • 拥有一个包含单个事件的仅附加表,以及一个包含相关键和值的聚合表。
  • 使用AbstractCloudBigtableTableDoFnPut 插入到仅附加表中。
  • 在同一数据流的顺序阶段,窗口事件在合理的短时间内(5 秒?)按聚合键分组。
  • 在该数据流的下一阶段(或者此时甚至可以将其路由到另一个数据流,真的),对仅附加表中的每个分组键执行范围扫描,聚合值并执行 @ 987654323@ 插入到该聚合表中。

这样:

  • 保证在AbstractCloudBigtableTableDoFn之后触发窗口时事件在Bigtable中,可以获得一致的聚合。
  • 在一个窗口在下一个触发窗口之后完成的不太可能的情况下(具有旧的聚合视图),触发时间戳可用于区分最新版本的单元格。
  • 仅附加表也可用于其他类型/级别的聚合,这些聚合可以在以后决定。
  • 维护相对简单,不需要任何定期清洁工作。

【讨论】:

    【解决方案3】:

    也许您可以尝试将每个事务更新添加为单独的列,使用时间戳作为限定符,这样总花费就是所有列的总和?您可以定期将 N 列压缩为一个时间戳,并且更新可以是原子的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-30
      • 2018-10-18
      • 2018-04-05
      • 2018-02-16
      • 2016-06-09
      • 2016-05-28
      相关资源
      最近更新 更多