【问题标题】:Advise to improve Query Performance (Oracle)建议提高查询性能 (Oracle)
【发布时间】:2018-11-27 12:18:46
【问题描述】:

我有一个包含 116,317,979 条记录的表 (A),并且它每天以每天大约 750,000 条记录的速度增长。

根据我的要求,我想使用日期列有效地从表中获取最后 3 天的数据(日期时间存储在列中)。所以查询将是

select * from A where date_column >= trunc(sysdate) - 3

我还需要将表 A 与表 B 连接起来,这样

select * from A 
left outer join B 
on A.X = B.X and A.Y = B.Y and A.Z = B.Z and B.M = 'XYZ' and B.N = 'UIM'
where A.date_column >= trunc(sysdate) - 3

表 B 的唯一索引 & PK (X,Y,Z,M,N)

表 A (ID) 的唯一索引 & PK


表 A 上的建议 IDX 1 (date_column)

表 A (X,Y,Z) 上的建议 IDX 2

Time without Indexes 34 sec
Time with IDX 1      32 sec
Time with IDX 1 & 2  27 sec //Sorry about the mistype

通过仅在 A.date_column 上添加索引,我认为我可以显着提高性能,但我的测试结果是否定的。除了添加新索引之外,还有其他提示可以提高性能吗?从长远来看,添加这样的索引有什么害处吗?

最好创建另一个表并以某种方式在其中填充最近 3 天的数据(使用 db 触发器)。我可以轻松地使用另一个流程来每晚清除超过 3 天的数据。

提前致谢。

【问题讨论】:

    标签: sql performance oracle11g


    【解决方案1】:

    您应该检查执行计划以查看索引是否正在使用。我猜date_column 上的索引没有被使用,32 秒和 34 秒之间的差异只是噪音。

    我建议在A(date_column, X, Y, Z) 上为此查询建立索引。

    添加索引有什么害处吗?好吧,他们在inserts/updates/deletes 上增加了开销。如果您的插入是事务性的,那么您每秒插入大约 10 行——不包括更新和删除。如果您的峰值明显高于并且您的硬件不是很好,那么索引可能会减慢速度。如果批量添加额外的行,我不会担心开销。

    我怀疑将表格拆分为单独的 3 天表格会产生很大的不同。但为什么要听我的?试试看。获取过去 3 天以上的数据,将其转储到表中,正确索引并查看查询是否更快。

    【讨论】:

    • 我的插入是分批进行的,但每 15 分钟发生近 6 次。
    • @pOrinG 。 . .如果插入发生的次数少于每分钟一次,我不会太担心索引开销。
    • 谢谢。你能解释一下关于开销的更多信息吗?您是说由于插入频繁发生,因此开销将微不足道,对吗?如果插入是大批量发生的,那会是一个更大的问题吗?
    • @pOrinG 。 . .我会担心多个查询之间的锁定争用。我的猜测是更新几行的索引可能需要一两秒钟。每 15 分钟左右的威胁并不大——而且它们不会相互干扰。如果您每秒进行多次插入,那么它们可能会相互干扰并干扰其他查询。当然,如果您的批次是 1,000,000 行,那么锁定会花费更长的时间并且可能是一个更大的问题。
    【解决方案2】:

    Oracle 分区在这里很有意义,但即使对于企业版,这也是一个额外的成本选项。 如果分区不可用 - 保留最后 3 天的单独表应该是最好的性能。你应该试试看。

    如果您想从索引中获得最大值,那么您可以考虑使用物理参数:

    • 如果日期列没有更新,数据很少被删除,那么可以设置PCTFREE 0
    • 查看您的最终查询,我建议在 trunc(date) 列上创建一个索引并使用压缩 -> 在这种情况下,每个索引数据块存储更多条目。在这种情况下,最终的查询条件应该是trunc(date_column) >= trunc(sysdate) - 3

    根据表 A 中 X、Y、Z 的选择性,压缩它们也是有意义的。所以我建议检查两种情况:

    1. create index trunc_date_ai on A(trunc(date_column)) pctfree 0 compress; + 你的 IDX2
    2. create index trunc_date_ai on A(trunc(date_column),X,Y,Z) pctfree 0 compress; pctfree 0 应该在 X、Y、Z 未在表 A 中更新的情况下使用。 compress 关键字在这里对所有 4 列进行压缩,因此对于特定 trunc(date_column),如果 X、Y、Z 值在表 A 中高度可重复,则值得使用。

    要强制使用索引,您可以提示查询,例如:

    select --+ index (A trunc_date_ai)
           * 
    from   A left outer join B 
    on A.X = B.X and A.Y = B.Y and A.Z = B.Z and B.M = 'XYZ' and B.N = 'UIM'
    where trunc(A.date_column) >= trunc(sysdate) - 3
    

    【讨论】:

    • 感谢您的回答,我做了一些测试,发现建议的案例 1 和 2 具有接近相似的性能,并且差异不大。我也没有使用pctfree 0 选项。添加压缩显着减少了存储中索引(trunc(date),x,y,z) 的大小,从4.349gb 减少到1.438gb。由于我们一天内添加了近 750000 条记录,因此 trunc(date) 会重复多次,甚至 x,y,z 也是高度重复的。在这种情况下使用压缩有什么副作用吗?
    • 我有另一个类似的环境,它的数据几乎比这个少 10 倍,拥有一个只有 3 天数据的单独表的性能会好得多,但是我担心会增加触发器插入的开销。触发器看起来像if date > sysdate - 3 then insert into new_table。让这条语句每天运行 750000 次是可怕的,它将完全消除我因批量插入 IMO 而获得的性能提升。但是在这种情况下我可能是非常错误的。你能建议吗?
    • 另外目前我的索引脚本显示Compress 4,oracle如何确定值4?
    • 副作用是数据更改(dml)的性能。所以最好估计这个效果。重新触发 - 我认为在插入时阻止触发是没有意义的。由于您以常规批次插入数据 - 那么我建议在每次批量插入后将小表刷新为单独的过程运行。为了使刷新更快并且在日期列未更改的情况下,您可以简单地保存表 A 的最后处理的 Id 值,并下次处理 A 中的记录,其中 id > saved_id_from_last_refresh_run。当然,您需要每天清理一次旧数据。希望对您有所帮助。
    • 压缩 4 是预期的。如果在create index语句中不指定压缩程度,那么所有的索引列都会被压缩。
    猜你喜欢
    • 1970-01-01
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-27
    • 2013-08-03
    • 2021-12-27
    相关资源
    最近更新 更多