提高在 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 查询的速度的主要内容,如果未能解决你的问题,请参考以下文章