我想更新一个大表(几千万行)中的一列,而又不降低应用程序的性能。即将有并发写入表。我想在代码(Java)中执行此操作,因为此更新非常简单,并且想知道执行此操作的最佳方法是什么。
一种有效的方法是打开一个查询所有行的读取事务,然后循环遍历所有这些行(resultSet.next()
),同时创建一系列具有10,000 {{1 }}在每个事务中缓冲的突变,包含所需的更新。
问题在于这不能处理并发写入,因为可能会发生以下步骤:
要解决此问题,我可以在读/写事务期间读回该值,并验证它是否未更改,类似于this example here,但是这似乎很慢(每个调用〜50 ms,这意味着要花几周的时间来更新整个表格)。
那么我如何才能更有效地做到这一点?谢谢!
答案 0 :(得分:2)
执行此操作的最佳方法不是使用只读事务,而是为每批10,000条记录启动读/写事务,在此读/写事务中读取要更新的值,然后在同一读取/写入事务中更新这10,000条记录。重复此操作,直到所有记录都已更新。
是这样的:
numeric(20, 4)
和LIMIT
限制结果,因此您将获得类似OFFSET
的查询答案 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的一个问题是,由于重试,该表达式将在每行上至少运行一次 -因此,该语句必须是幂等的,即多次执行时给出相同的结果