提高在 ExaData 上运行的 Oracle 查询的速度

Posted

技术标签:

【中文标题】提高在 ExaData 上运行的 Oracle 查询的速度【英文标题】:Increase speed of Oracle query running on ExaData 【发布时间】:2014-08-22 13:43:36 【问题描述】:

我正在处理一个 Oracle 查询,我迫切需要让它运行得更快。我将不胜感激任何建议。

数据库是 Oracle,在 ExaData 集群上运行。 Oracle 版本:Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production

我有两张桌子。

1) 交易:在商店购买 - 交易ID

2) TransactionItems:每次购买有 1..many items - TransactionID, ItemID

在每个表中,有两个标志:

标志A:是/否 标志B:是/否

查询需要:

    为 TransactionItem 中的每条记录设置 FlagA 和 FlagB 的值。 根据 TransactionItem 中 Flags 的值,为 Transaction 中的每一行设置 FlagA 和 FlagB 的值

我已将查询分为 4 个步骤。

    为 TransactionItem 设置标志 A 的值 为 TransactionItem 设置标志 B 的值 为事务设置标志 A 的值 为事务设置标志 B 的值

查询运行顺利。然而,这就是问题所在。有数十亿条Transaction记录,每个Transaction大约有7个Transaction Item。

这是现在的速度:

总时间:616 秒 / 10.27 分钟 每秒处理 1,218 个事务/每分钟 73,000 个事务

我跟踪了每个步骤的处理时间:

    为TransactionItem设置Flag A的值

    4 分 52 秒

    为TransactionItem设置标志B的值

    3 分 26 秒

    为事务设置标志A的值

    1 分 6 秒

    为事务设置标志B的值

    0 分 51 秒

以下是我的完整查询。以下是使用的其他表格

产品

每个 TransactionItem 都有一个 ProductId 每个产品都有一个 ProductCode。 一个产品代码有多个产品

标志产品代码

    包含分类为的 ProductCode 列表的单个列 标志A

标志产品代码

    包含分类为的 ProductCode 列表的单个列 标志B

交易支付

    这是一个包含每笔交易的付款详情的事实表

Payment_Dim

    PaymentID 上的 TransactionPayment 链接 这是必需的,因为 FlagB 是根据 Payment_Dim.PaymentName 设置的

我有这些索引:

交易 1. 交易ID

交易项目 1.交易ID 2.产品ID

产品 1.产品ID 2.产品代码

标记产品代码 1.产品代码

标志B产品代码 1.产品代码

付款 1.支付ID 2. 支付代码 3. Payment_Name

非常感谢您的帮助,谢谢

-- 1.   Set value of FlagA for TransactionItem

Update
          TransactionItems  Item

Set FlagA = 
      (
          Select 
                    Case 


                    When  
                              Item.FlagA_Qty = 0 Then 'N' -- this is the quantity of items purchased that fall into the FlagA category

                    When 
                              FlagA.ProductCode Is Null Then 'N'  

                    Else
                               'Y'

                    End 

          From  
                    Product Prod 
                    Left Join 
                              FlagAproductCodes FlagA 
                                        On Product.ProductCode = FlagA.ProductCode   


          Where  
Product.Prod_Id = Item.Prod_Id                    

        )
;



-- 2.  Set value of FlagB for TransactionItem

Update TransactionItems

Set  FlagB = 'Y'

Where ItemID In 
          (
          Select 
                    Trans_Items.ItemID
          From 
                    TransactionItems Trans_Items
                    Inner Join Product Prod 
                              On Trans_Items.Prod_Id = Product.Prod_Id 
                    Inner Join FlagBproductCodes FlagB 

                              On Product.ProductCode = FlagB.ProductCode
          Where 
                    (
                              Trans_Items.Gov_FlagA_Qty < Trans_Items.Item_Qty
                    ) 
                    AND 
                    (
                              Exists 
                              (Select Transaction_Payment_Fid 
                              From TransactionPayment Trans_Pay

                                        Inner Join Warehouse.Payment_Dim Pay_Dim 
                                                  On Trans_Pay.Payment_Id = Pay_Dim.Payment_Id
                              Where 
                                        Transaction_Fid = Trans_Items.Transaction_Fid 
                                        And Upper(Pay_Dim.Payment_Name) Like '%ABC%'
                              )
                    )
          )          
; 
Update TransactionItems
Set FlagB = 'N'
Where FlagB Is Null;






-- 3: Set FlagA for Transactions


Update
          Transactions  

Set 
          Gov_FlagA_Flag =
                    Case When Exists 
                              (Select ItemID 
                              From TransactionItems Item 
                              Where Item.Transaction_Fid = Transactions.Transaction_Fid 
                              and gov_FlagA_flag = 'Y') 
                              Then 'Y'
                    Else 'N'
                    End
;


-- 4: Set FlagB for Transactions


Update
          Transactions  

Set 
          FlagB =
                    Case When Exists 
                              (Select ItemID 
                              From TransactionItems Item 
                              Where Item.Transaction_Fid = Transactions.Transaction_Fid 
                              And FlagB = 'Y') 
                              Then 'Y'
                    Else 'N'
                    End
;

【问题讨论】:

更新每一行一次而不是两次可能会有所帮助;你为什么要这样把他们分开?完成后查看执行计划。例如,是否有任何并行性,是否有帮助? (它真的需要更快吗?这听起来像是一次性的任务;如果您尝试定期维护这些标志,那么使用视图甚至物化视图可能会更好)。 您好 Alex,感谢您的回复。 您好亚历克斯,感谢您的回复。我需要更新大约 140 亿个历史交易,这些交易有超过 1000 亿个与之关联的 TransactionItem。是的,这是一次性的。当前查询需要 6 个月,每晚运行 8 小时。我一直在玩Parallel,但我对此了解不多。即“更改会话启用并行 Dml;”在一个完美的世界中,我会进行一次更新来为 Transaction Items 表设置 FlagA 和 FlagB,然后再进行一次更新以在 Transactions 表中设置 Flags。我一直不知道怎么写。 【参考方案1】:

您需要研究并行执行,这可能是一个过于宽泛的主题,无法在此处充分探讨。 (而且我没有资格多说)。

与此同时,您可能会通过只更新每个表一次并减少偶然查找的次数来获得一些好处。这未经测试,但我认为涵盖与您针对 TransactionItems 的三个更新相同的逻辑,例如:

merge into TransactionItems TI
using (
  select P.Prod_ID,
    case when FAPC.ProductCode is null then 'N' else 'Y' end as FlagA,
    case when FBPC.ProductCode is null then 'N' else 'Y' end as FlagB
  from Product P
  left join FlagAproductCodes FAPC on FAPC.ProductCode = P.ProductCode
  left join FlagAproductCodes FBPC on FBPC.ProductCode = P.ProductCode
) temp
on (temp.Prod_id = TI.Prod_ID)
when matched then
update set TI.FlagA = case when temp.FlagA = 'Y' and TI.FlagA_Qty != 0
    then 'Y' else 'N' end,
  TI.FlagB = case when TI.FlagA_Qty < TI.Item_Qty
    and exists (
      select Transaction_Payment_Fid 
      from TransactionPayment TP
      join Payment_Dim PD on TP.Payment_Id = PD.Payment_Id
      where TP.Transaction_Fid = TI.Transaction_Fid 
      and upper(PD.Payment_Name) Like '%ABC%'
    ) then 'Y' else 'N' end
/

您可能更喜欢创建可更新的视图。但在如此庞大的数据量上,仍然需要很长时间。

This might also be useful.

【讨论】:

【参考方案2】:

有趣的挑战。我的直接反应是分而治之——编写 PLSQL 以对扇区/ID 范围进行操作,并经常提交。然后启动并行作业以在不同范围内运行,然后调整以找到最佳设置。如果运气好的话,表是分区的,那就更好了。

另外,虽然我来自一个一切都是基于集合完成的时代,但在 PLSQL 甚至还没有被梦想过之前,您可能想要考虑在事务的基础上重新设计它,而不是您当前的基于集合的方法,从而您获取更新主行作为批量收集,然后使用批量收集来驱动详细表更新。我发现这可以更快,并且它确实让您在批量处理此操作时有更多的控制权。如果应该失败,这也将为您提供重新启动的选项,例如快照太旧,存档日志已满等。如果这发生了爆炸,您将不想重新开始。

【讨论】:

【参考方案3】:

我非常感谢您的指导 - 这真的很有帮助!

启用并行带来了巨大的变化!

ALTER SESSION 启用并行 DML;

再次感谢大家的帮助

【讨论】:

以上是关于提高在 ExaData 上运行的 Oracle 查询的速度的主要内容,如果未能解决你的问题,请参考以下文章

Exadata想要补装Oracle 11g的注意事项

ORACLE EXADATA 12.1.2.3.7 虚拟环境模拟 (一)

Exadata 12.2.1.1.0 Highlights

Oracle18c Exadata 版本安装介质安装失败。

Exadata SL6 是个什么鬼?

Exadata使用技巧 (-)