如何在不制作大的临时日志的情况下正确更新大表?

How to properly update big tables without making big transient journal?

我有 table 个名称 tb_big,大小为 50 Gb。如果创建附加列:

alter TABLE UAT_DM.ai_SUBS_MONTH_CLR ADD segment CHAR(5) not casespecific;

一切正常。所有的值都是空的,所以更新会很快(1 分钟以上):

update tb_big
set segment = case when LT_month <= 4 then '0'
     when LT_month <= 8 then '1'
     when LT_month <= 12 then '2'
     when LT_month <= 17 then '3'
     when LT_month <= 24 then '4'
     when LT_month <= 36 then '5'
     when LT_month <= 56 then '6'
     when LT_month <= 83 then '7'
     when LT_month <= 96 then '8'
     else  '9' end;

假设我想在同一列中第二次更新它:

update tb_big
    set segment = case when LT_month <= 4 then '0'
         when LT_month <= 8 then '1'
         when LT_month <= 12 then '2'
         when LT_month <= 17 then '3'
         when LT_month <= 27 then '42'
         when LT_month <= 36 then '52'
         when LT_month <= 56 then '6'
         when LT_month <= 83 then '7'
         when LT_month <= 96 then '08'
         else  '9' end;

因为大 table 大小和一些意外的 TD 行为,这样的更新将在事务下工作,所以每次更新都会被记录到临时日志中,由于我未知的原因,这将是高度倾斜的(99.9%+ ) 并获取数 TB 的假脱机。我几乎让生产服务器跪下(TD 管理员没有关闭它并希望它完成并且备份失败,因为回滚可能需要很长时间。是真的吗?)

我的问题是如何正确更新大 tables?我的想法是删除该专栏并从头开始重复。但是我担心我不会再出现假脱机错误 (see )。一个好的可能解决方案可能是创建新的空 table 并从第一个 table 复制所有列,除了要修改的列。但是服用 x2 space 不是一个好习惯。

TD 管理员的建议不要更新超过 20 万行对我来说听起来很荒谬。

table 的 DDL,它有 5 亿行,大小 50 Gb:

CREATE MULTISET TABLE UAT_DM.ai_SUBS_MONTH_CLR ,NO FALLBACK ,
     NO BEFORE JOURNAL,
     NO AFTER JOURNAL,
     CHECKSUM = DEFAULT,
     DEFAULT MERGEBLOCKRATIO
     (
      CUST_ID DECIMAL(12,0),
      LT_month DECIMAL(15,2),
      days_to_LF(15,0),
      REV_COM DECIMAL(18,6),
      device_type VARCHAR(50) CHARACTER SET UNICODE CASESPECIFIC,
      usg_qq DECIMAL(18,0),
      usg_dd DECIMAL(18,6),
      report_mnth CHAR(7) CHARACTER SET UNICODE NOT CASESPECIFIC,
      MACN_ID DECIMAL(15,0),
      segment CHAR(5) CHARACTER SET LATIN NOT CASESPECIFIC)
UNIQUE PRIMARY INDEX ( SUBS_ID ,report_mnth )
INDEX ( CUST_ID )
INDEX ( segment );

你没有说出一个很重要的细节。

您首先添加了新列 segment,然后对其进行了更新。

然后您在 segment 上创建了二级索引。

然后你运行第二次更新。

由于不同值的数量很少,因此这个 NUSI 非常小。由于重复值的数量很多,维护成本也相当高(我从未测试过,但这也应该是 Transient Journal 倾斜的原因)。

有一个经验法则(在大多数(所有?)DBMS 中都是相似的):当它是索引列时,不要更新大部分行。

你通常会这样做一次,然后你会接到 DBA 的电话,你将永远不会忘记:-)(当然它不应该是 你最好分批更新 )

在更新之前删除索引,然后重新创建它。

但是为什么要在 segment 上放置二级索引?由于它的选择性低,它可能不会被优化器使用。当然,您可以将 CASE 作为计算而不是列。

并且假设 SUBS_IDCUST_ID 相同,您可以通过将主索引更改为 PRIMARY INDEX ( SUBS_ID ) 来摆脱第二个 NUSI,行数可能不会很多每 SUBS_ID。如果您使用 report_mnth 进行大量访问,则可以按它进行分区(然后最好将其更改为 INT,201805 而不是“2018-05”)。