某物流客户系统查询快递单的SQL,IO消耗为TOP 1:
sql
SQL代码以下:数据库
select id,性能优化
op_code,微信
to_char(create_time, :"SYS_B_1") as create_time,oracle
……运维
from T_EXP_OP_RECORD_CONTAINER A 性能
where status <> :"SYS_B_4" and ID = :1 and rownum = :"SYS_B_5";优化
其中T_EXP_OP_RECORD_CONTAINER 表是一个在Create_time字段按天一级分和op_code字段按地区二级分区的分区表,ID字段保存的是快递单号信息,字段上存在索引。spa
SQL代码中出现了"SYS_B_n" 字样的绑定变量,这是由于数据库参数的cursor_sharging被设置为FORCE(强烈建议保持默认值EXACT),SQL中使用的常量值被强制转换成了绑定变量。rownum=后面的常量被强制转换成了绑定变量,这个值根据常识能够判断为1,由于只有1才有意义。.net
快递单号基本上是惟一的,这样的SQL,正常执行时间应该在1毫秒左右。
而下图使用awrsqrpt收集的SQL实际执行状况是:每次执行耗时1.236秒。
SQL执行计划以下:
看到上面的执行计划后,就会明白平均执行时间是1秒多就正常了:这个查询要到6030个local index里面检索数据,平均每一个local index至少要扫描3个buffers 才能判断记录是否存在,由于有rownum=1 谓词条件,最好的状况是扫描local index的第一个分支就找到告终果,再也不继续扫描下去;最差的状况是扫描到local index的最后一个分支才找到结果,或是没有找到结果。
通常状况下,local index索引的使用,须要配合分区字段一块儿作谓词条件,才能只扫描少数的索引分支。而这个SQL因为业务缘由,不能增长分区字段做为谓词条件。这种状况就须要将local index改为Global index,才会使SQL性能达到最佳。
可是,由于该表很是庞大(表和索引占用的空间达到T级),须要按期删除(转移)历史分区,只保留最近一年的数据,若是建立的是global index,删除历史分区后,须要对global index进行重建,维护时间窗口很难完成(有多个相似表)。这是个两难的问题。
针对快递业务的特色,老虎刘给出的建议是:
一、仍使用local index,重建表,减小分区数量:按天分区改成按月分区,不要子分区;
二、由于不多有用户会查询1个月以上的快递单,该表只保留最近2个月分区数据,其余数据转移到历史分区,正常状况只须要最多扫描2个分区,而不是原来的6030个分区。
三、经过plsql实现查询:当前分区没有查询到结果,再去查询历史分区。这样也能保证超过2个月的快递单也能正常查询。
总结:
分区表,到底选择global index仍是local index,须要根据具体的业务和运维的实际需求而定。不须要删除历史分区数据的分区表,能够建立global index(如基础数据表);须要按期删除历史分区的分区表,最好是建立local index,若是遇到分区字段没法成为查询条件时,建议尽可能减小分区数,避免过多的local index 扫描,影响SQL性能。
关注“老虎刘谈SQL优化”,分享老虎刘那些年的SQL优化案例!
脚本分享在QQ群:16778072
欢迎转发分享给更多的朋友
为了方便交流,有兴趣的朋友能够加入同名微信群:
本文分享自微信公众号 - 老虎刘谈oracle性能优化(sql_tigerliu)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。