【问题标题】:Row level atomic MERGE REPLACE in BigQueryBigQuery 中的行级原子合并替换
【发布时间】:2018-07-04 10:29:48
【问题描述】:

对于我的用例,我正在处理可通过源中唯一键识别的数据,这些数据会分解为 n(非确定性)数量的目标条目,这些条目已加载到 BigQuery 表中以进行分析。

构建此 ETL 以使用 Mongo 最近的更改流功能我想删除 BigQuery 中的所有条目,然后自动加载新条目。

探索 BigQuery DML 我看到支持 MERGE 操作,但只有 WHEN MATCHED THEN DELETEWHEN MATCHED THEN UPDATE 是可能的。

我对 WHEN MATCHED THEN DELETE,然后是 INSERT 操作感兴趣。

我如何在 BigQuery 中实现此类 ETL,同时在数据可用性和正确性方面尽可能保持原子性或最终一致。


编辑1:我想提供一个具体的例子来详细说明。

我在这个数据集上拥有的最小唯一性粒度是user_id。行不是唯一可识别的。

示例

1.

从 mongo 更改流接收的更新用户对象:

user={_id: "3", name="max", registered="2018-07-05" q=["a", "b", "c"]}

2.

当前 BigQuery.user_q 保留

| user_id | q |
...
|    3    | a |
|    3    | b |
...

3.

转换代码将修改后的用户对象加载到 BigQuery.user_q_incoming

| user_id | q |
|    3    | a |
|    3    | b |
|    3    | c |

4.

user_quser_q_incoming 之间的合并:

  1. user_q 中属于 user_id 3 的 2 行被删除
  2. user_q_incoming 中属于 user_id 3 的 3 行已插入。
  3. user_q 中的其余数据 (...) 保留在原处,未修改。

5.

BigQuery.user_q 保留

| user_id | q |
...
|    3    | a |
|    3    | b |
|    3    | c |
...

例如,用户可能会从他的个人资料中删除一个问题。将剩余的行保留为q=["a", "c"]。我也需要将其转化为 BigQuery 的世界观。

【问题讨论】:

    标签: mongodb google-bigquery etl acid


    【解决方案1】:

    BigQuery DML 支持 INSERT

    MERGE 语句是一种 DML 语句,它可以将 INSERT、UPDATE 和 DELETE 操作组合到一个语句中并以原子方式执行这些操作。

    例如

    MERGE dataset.Inventory T
    USING dataset.NewArrivals S
    ON FALSE
    WHEN NOT MATCHED AND product LIKE '%washer%' THEN
      INSERT (product, quantity) VALUES(product, quantity)
    WHEN NOT MATCHED BY SOURCE AND product LIKE '%washer%' THEN
      DELETE   
    

    所以,你应该很好地使用你的 ETL

    根据添加到问题的更具体细节进行编辑

    好的,我明白了 - 我认为在这种情况下 MERGE 将不适用,因为 INSERT 只能用于 NOT MATCH 子句。有人可能会想出如何欺骗 MERGE 在这种情况下工作,但与此同时,下面的解决方案可以实现您想要实现的目标 - 我认为是这样 :o)

    CREATE OR REPLACE TABLE `project.dataset.user_q` (user_id INT64, q STRING) AS
    SELECT * FROM `project.dataset.user_q`
    WHERE NOT user_id IN (SELECT DISTINCT user_id FROM `project.dataset.user_q_incoming`)
    UNION ALL
    SELECT * FROM `project.dataset.user_q_incoming`
    WHERE user_id IN (SELECT DISTINCT user_id FROM `project.dataset.user_q`)
    

    【讨论】:

    • 米哈伊尔,请参阅我的编辑描述我想到的合并操作。
    • 我现在明白了,这与我最初阅读您的问题的方式“有点”不同。请参阅我的回答中的更新-希望它现在对您有用:o)还要注意,即使它正式按照您的要求进行-但请考虑cost of DML operations
    • @MaximVeksler - 你有机会尝试吗?
    【解决方案2】:

    有一个类似的问题和一个让 MERGE 工作的方法 (https://issuetracker.google.com/issues/35905927#comment9)。

    基本上,像下面这样的东西应该可以工作,

    MERGE `project.dataset.user_q` T
    USING (
      SELECT *, false AS is_insert FROM `project.dataset.user_q_incoming`
    UNION ALL
      SELECT *, true AS is_insert FROM `project.dataset.user_q_incoming`
    ) S
    ON T.user_id = S.user_id and NOT is_insert
    WHEN MATCHED THEN
      DELETE
    WHEN NOT MATCHED AND is_insert THEN
      INSERT(user_id, q) VALUES(user_id, q)
    

    理想情况下,以下是您需要的,但尚不支持。

    MERGE `project.dataset.user_q`
    USING `project.dataset.user_q_incoming`
    ON FALSE
    WHEN NOT MATCHED BY TARGET THEN
      INSERT(user_id, q) VALUES(user_id, q)
    WHEN NOT MATCHED BY SOURCE AND user_id in (SELECT user_id FROM `project.dataset.user_q_incoming`) THEN
      DELETE
    

    【讨论】:

    • 谢谢。 UNION 的性能或一般的预期性能如何?副作用:目前我正在执行 MERGE DELETE 然后加载 gist.github.com/maximveksler/c4c708ad792672ada39c5dd2dc3637c3 这不是原子的,也不是一个好的解决方案,但它允许我执行“INSERT *”,而不必指定 INSERT(字段... )。我可以为合并做同样的事情吗?
    • Maxim,我在issuetracker.google.com/issues/35905927#comment14 发表了评论,建议使用 CROSS JOIN UNNEST([true, false]) 而不是 UNION ALL,这样可以提供更好的性能。不幸的是,“INSERT *”不支持它。我们知道这样的不便。一旦有更好的解决方案,将提供更新。
    • 如果您使用分区并且已超过限制 (1000),这将不起作用 :(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 2021-09-28
    • 2019-11-21
    • 2014-10-11
    相关资源
    最近更新 更多