2017-10-05 67 views
1

我有这个蜂巢MERGE语句运行: 我的SQL MERGE语句太久

MERGE INTO destination dst 
USING (
    SELECT 

    -- DISTINCT fields 
     company 
    , contact_id as id 
    , ct.cid as cid 

    -- other fields 
    , email 
    , timestamp_utc 
    -- there are actually about 6 more 

    -- deduplication 
    , ROW_NUMBER() OVER (
     PARTITION BY company 
     , ct.id 
     , contact_id 
     ORDER BY timestamp_utc DESC 
    ) as r 

    FROM 
    source 
    LATERAL VIEW explode(campaign_id) ct AS cid 
) src 
ON 
     dst.company = src.company 
    AND dst.campaign_id = src.cid 
    AND dst.id = src.id 

-- On match: keep latest loaded 
WHEN MATCHED 
    AND dst.updated_on_utc < src.timestamp_utc 
    AND src.r = 1 
THEN UPDATE SET 
    email = src.email 
    , updated_on_utc = src.timestamp_utc 

WHEN NOT MATCHED AND src.r = 1 THEN INSERT VALUES (
    src.id 

    , src.email 

    , src.timestamp_utc 

    , src.license_name 
    , src.cid 
) 
; 

这对于一个很长的时间(30分钟Avro的压缩数据的7GB在磁盘上)上运行。 我不知道是否有任何SQL方法来改善它。

ROW_NUMBER()在这里对源表进行重复数据删除,因此在MATCH子句中我们只选择最早的行。

有一两件事我不知道的是,hive says

SQL标准要求,如果ON子句是这样 超过1行中源目标相匹配的行抛出一个错误。此检查为 计算量很大,并且可能会显着影响MERGE语句的整体运行时间。 hive.merge.cardinality.check = false可能使用 禁用检查,风险自负。如果检查是 禁用,但声明具有这种交叉连接效果,则可能导致 数据损坏。

我确实禁用了基数检查,因为虽然ON语句可能在源中提供2行,但由于MATCH子句后面的r = 1,所以这些行仅限于1。

总的来说,我喜欢这个MERGE语句,但它太慢了,任何帮助,将不胜感激。

请注意,目标表是分区的。源表不是因为它是每个运行必须完全合并的外部表,所以完全扫描(在后台已经合并的数据文件被删除并且在下次运行之前添加新文件)。不知道分区会在这种情况下

我所做的帮助:

  • 与HDFS /蜂巢/纱构造发挥
  • 尝试用临时表(2级),而不是一个单一的MERGE,运行时间跳到了2个多小时。

回答

1

选项1:移动,其中过滤where src.r = 1src子查询中,并检查合并性能。这将减少合并之前的源行数。

其他两个选项不需要ACID模式。做完整的目标重写。

选项2:重写使用UNION ALL + ROW_NUMBER(这应该是最快的国家之一):

insert overwrite table destination 
select 
company 
, contact_id as id 
, ct.cid as cid 
, email 
, timestamp_utc 
, -- add more fields 
from 
(
select --dedupe, select last updated rows using row_number 
s.* 
, ROW_NUMBER() OVER (PARTITION BY company, ct.id , contact_id ORDER BY timestamp_utc DESC) as rn 
from 
(
select --union all source and target 
company 
, contact_id as id 
, ct.cid as cid 
, email 
, timestamp_utc 
, -- add more fields 
from source LATERAL VIEW explode(campaign_id) ct AS cid 
UNION ALL 
select 
company 
, contact_id as id 
, ct.cid as cid 
, email 
, timestamp_utc 
,-- add more fields 
from destination 
)s --union all 
where rn=1 --filter duplicates 
)s-- filtered dups 

如果source包含了很多重复的,可以申请额外ROW_NUMBER过滤到src子查询作为在工会之前。采用全

还有一个办法加入:https://stackoverflow.com/a/37744071/2700344

+0

谢谢,我运行性能测试与你不同的选择。我想知道插入覆盖 - 如果目标表(分区)有数百万行会发生什么?我觉得合并(例如)100k行到100M行要比插入100M行更快? – Guillaume

+0

@Guillaume请分享你的结果。我在Hive 1.2上使用第二个选项。 – leftjoin

+0

选项的问题:〜25-30分钟,你的选项1(src.r = 1在子查询中):30-35分钟,你的选项2(无酸,插入覆盖):〜32分钟,临时表:2h +。你的源/目的表有多大? – Guillaume