【问题标题】:My SQL MERGE statement runs for too longMysql MERGE 语句运行时间过长
【发布时间】:2017-10-05 14:01:55
【问题描述】:

我有这个 Hive 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
)
;

运行时间很长(磁盘上 7GB 的 avro 压缩数据需要 30 分钟)。 不知道有没有什么SQL方法可以改进一下。

ROW_NUMBER() 在这里是为了对源表进行去重,所以在 MATCH 子句中我们只选择最早的行。

我不确定的一件事是hive says

如果 ON 子句是这样的,SQL 标准要求引发错误 源中超过 1 行与目标中的行匹配。这个检查是 计算成本高,可能会影响一个整体的运行时间 MERGE 语句显着。 hive.merge.cardinality.check=false 可能 用于禁用检查,风险自负。如果支票是 disabled,但是语句有这样的交叉连接效果,可能会导致 数据损坏。

我确实禁用了基数检查,因为尽管 ON 语句可能会在源代码中提供 2 行,但这些行仅限于 1,这要归功于 MATCH 子句后面的 r=1。

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

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

我做了什么:

  • 玩转 hdfs/hive/yarn 配置
  • 尝试使用临时表(2 步)而不是单个 MERGE,运行时间跃升至 2 多个小时。

【问题讨论】:

    标签: hive sql-merge


    【解决方案1】:

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

    其他两个选项不需要 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 包含大量重复项,您可以在联合之前对 src 子查询应用额外的 row_number 过滤。

    另一种使用完全联接的方法: https://stackoverflow.com/a/37744071/2700344

    【讨论】:

    • 谢谢,我正在使用您的不同选项进行性能测试。我想知道插入覆盖 - 如果目标表(分区)有数百万行会发生什么?我会觉得将 100k 行合并(例如)100M 行比插入覆盖 100M 行要快?
    • @Guillaume 请分享您的结果。我在 Hive 1.2 上使用第二个选项。
    • 问题中的选项:~25-30 分钟,您的选项1(子查询中的 src.r=1):30-35 分钟,您的选项 2(无酸,插入覆盖):~32 分钟,临时表:2h+。您的源/目标表有多大?
    • ~50M 运行大约 10 分钟。
    • 您运行的是 Tez 还是 MR?我在 Tez 上运行
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-19
    • 2023-03-16
    • 2016-11-30
    相关资源
    最近更新 更多