通过并发写入来更新大型Spanner表中的列

时间:2019-07-09 23:40:38

标签: google-cloud-spanner

我想更新一个大表(几千万行)中的一列,而又不降低应用程序的性能。即将有并发写入表。我想在代码(Java)中执行此操作,因为此更新非常简单,并且想知道执行此操作的最佳方法是什么。

一种有效的方法是打开一个查询所有行的读取事务,然后循环遍历所有这些行(resultSet.next()),同时创建一系列具有10,000 {{1 }}在每个事务中缓冲的突变,包含所需的更新。

问题在于这不能处理并发写入,因为可能会发生以下步骤:

  1. 上述读取事务读取第X行
  2. 一些单独的交易更新第X行
  3. 上述读/写事务使用步骤1中的数据并覆盖步骤2中的更新

要解决此问题,我可以在读/写事务期间读回该值,并验证它是否未更改,类似于this example here,但是这似乎很慢(每个调用〜50 ms,这意味着要花几周的时间来更新整个表格)。

那么我如何才能更有效地做到这一点?谢谢!

2 个答案:

答案 0 :(得分:2)

执行此操作的最佳方法不是使用只读事务,而是为每批10,000条记录启动读/写事务,在此读/写事务中读取要更新的值,然后在同一读取/写入事务中更新这10,000条记录。重复此操作,直到所有记录都已更新。

是这样的:

  1. 开始读/写事务
  2. 读取10,000条记录的批次。通过对主键或某些其他唯一(组合)列进行排序,确保记录的顺序一致。通过使用numeric(20, 4)LIMIT限制结果,因此您将获得类似OFFSET的查询
  3. 更新记录并提交交易。
  4. 重复直到所有记录都更新。

答案 1 :(得分:0)

您没有说要更新该列的方式-是设置常量值,还是基于行中的其他列计算值。

您也不会说该表的其他更新是什么,但是我认为它们将涉及对该列的修改或影响该列的其他列的修改。

无论哪种方式,partitioned DML is a solution to this ... 以更新语句的形式表示您的修改:

UPDATE table SET col1=123 WHERE col2=TRUE

然后将其作为分区DML(使用API​​或gcloud with the --enable-partitioned-dml flag)运行。Spanner会将操作拆分为多个单独的事务,其中每个事务在内部都是一致的。每个DML事务在运行事务时只会锁定表中行的子集。

分区DML的一个问题是,由于重试,该表达式将在每行上至少运行一次 -因此,该语句必须是幂等的,即多次执行时给出相同的结果