如何在不制作大的临时日志的情况下正确更新大表?
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_ID
与 CUST_ID
相同,您可以通过将主索引更改为 PRIMARY INDEX ( SUBS_ID )
来摆脱第二个 NUSI,行数可能不会很多每 SUBS_ID。如果您使用 report_mnth
进行大量访问,则可以按它进行分区(然后最好将其更改为 INT,201805 而不是“2018-05”)。
我有 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_ID
与 CUST_ID
相同,您可以通过将主索引更改为 PRIMARY INDEX ( SUBS_ID )
来摆脱第二个 NUSI,行数可能不会很多每 SUBS_ID。如果您使用 report_mnth
进行大量访问,则可以按它进行分区(然后最好将其更改为 INT,201805 而不是“2018-05”)。