SQL语句优化,看到性能瓶颈。
春日东风, 拂面无寒。 油菜花香,微风拂过泛起点点菜想。 每年此时哥应该在湖边, 望着悠悠水草,阵阵涟漪之中的浮漂。
点点动作,便是激动上鱼时刻的前奏。 时间如水流飞逝, 却百无聊赖。 都会想到 我是谁, 我干什么的。
就在那时说说一个SQl优化吧。
INSERT INTO INST.A_CARD_NUMBER_BLACKLIST
SELECT O.PARTY_ID,
OS1.PROD_ID,
OM.OFFER_ID,
OS.OFFER_SPEC_ID,
OS.STATUS_CD,
SYSDATE
FROM INST.OFFER_MEMBER OM,
INST.OFFER O,
SPEC.OFFER_SPEC OS,
INST.OFFER_SERV OS1
WHERE OS1.PROD_ID IN (SELECT PROD_ID
FROM INST.OFFER_SERV
WHERE SERV_SPEC_ID = 1537
AND STATUS_CD = 12)
AND OM.MEMBER_ID = OS1.SERV_ID
AND OM.STATUS_CD = 12
AND OM.OBJ_TYPE = 4
AND OS.OFFER_SPEC_ID = O.OFFER_SPEC_ID
AND OS.OFFER_SPEC_ID NOT IN (300500003485,
300509027473,
300006000633,
300509027536,
300006000639,
300006000009,
300006000020)
AND OM.OFFER_ID = O.OFFER_ID
AND OS1.STATUS_CD = 12
AND OM.CREATE_DT >
(SELECT MAX(VERSION) FROM INST.A_CARD_NUMBER_BLACKLIST)
UNION
SELECT O.PARTY_ID,
OM.MEMBER_ID,
OM.OFFER_ID,
OS.OFFER_SPEC_ID,
OS.STATUS_CD,
SYSDATE
FROM INST.OFFER_MEMBER OM, INST.OFFER O, SPEC.OFFER_SPEC OS
WHERE OM.MEMBER_ID IN (SELECT PROD_ID
FROM INST.OFFER_SERV
WHERE SERV_SPEC_ID = 1537
AND STATUS_CD = 12)
AND OM.STATUS_CD = 12
AND OM.OBJ_TYPE = 2
AND OS.OFFER_SPEC_ID = O.OFFER_SPEC_ID
AND OS.OFFER_SPEC_ID NOT IN (300500003485,
300509027473,
300006000633,
300509027536,
300006000639,
300006000009,
300006000020)
AND OM.OFFER_ID = O.OFFER_ID
AND OM.CREATE_DT >
(SELECT MAX(VERSION) FROM INST.A_CARD_NUMBER_BLACKLIST);
AWR 报告中一个SQl。 1小时执行不完。 分析SQL,哥发现主要是因为大表多次扫描导致, 有没有办法等价改写,把SQl中两次扫描变成一次呢?? 答案是肯定有办法的, 但是哥试着改写,发现下手确实困难。 算了想另外的办法把。
OFFER_MEMBER OM 20G
INST.OFFER 27G
SPEC.OFFER_SPEC OS, --- 小表
INST.OFFER_SERV 17G
执行计划:
你能一眼看到性能瓶颈吗? 如果不能,你还需要修炼。
信不信有你,性能问题在id = 7,20 . 因为这个表是大表,全表扫描。
有人说性能问题在id = 1 sort union . 这个去重操作, 执行计划中tempspc 到达1千多K, 另外还有cost 最高。 TIME这一步得耗费2小时。貌似很有道理, 但是哥说,如果“有人”从这一步下手了, 那“有人”优化还不过关, 需要修炼。 “有人”说错了, 大错特错。 为啥错?? 这个问题很简单,各位想想就通了,不说了。
哥看到这个 id = 7,20 。根据rows不多。CBO 选择了子查询作为驱动表, NL 外面的大表。CBO很聪明。
哥毫不犹豫的查询SQL。 SELECT /*+ parallel(t,8) */ count(1) FROM INST.OFFER_SERV t WHERE SERV_SPEC_ID = 1537 AND STATUS_CD = 12;查询了5分钟。 题外问题为何哥开了8个并行? 另外如何查询SQL执行时候到底开了几个并行? 有没有想过类似问题? 8011 条数据。
那哥更加确定了,肯定可以完美优化。 我知道你们认为是建索引, 的确建索引也是最好的办法, 但是问题在生产库,不和业务通知下就建索引?肯定不可行的。 哥可以建议建索引, 但是现在不要建索引优化。
那就用这个驱动表驱动下去,有人会问原来的也是这样的, 为啥会慢了?? 哥用的办法不同。
而且哥提醒, 不要随便在SQl中添加 并行, 哥在主要查询中特意把并行关了。 执行计划就不写了。 这样驱动表 只查询了一次
记好这个hints, 生产库中一时半会不能建索引,这招还真的管用。
猜猜看多久? 292S(没有建索引情况下) 。 1小时多没有执行完, (哥估计需要2小时20分钟)现在优化到 300S之内。
意不意外?惊不惊喜?
那我的建议是 1 在表 OFFER_SERV 建索引,当然了, 你要考虑这两个 SERV_SPEC_ID, TATUS_CD , 那个在前面, 另外还需要考虑 PROD_ID 字段是否需要包含在索引中,以及在索引中的位置。 这个涉及数据库对象的设计了, 以后的文章慢慢说吧。
2 共同的驱动表提取出来,组成临时表, 临时表驱动下去......
那问题来了? 如果哥再一次提高性能咋办?(工作中优化后快了就行,不要纠结, 哥以前纠结过总想把SQl优化到极致,成本最小,效果最大,结果 同时服务3,4个项目组, 也就是 5,6个人问SQL如何优化, SQl如何写, 复杂PLSQL写优化, 等乱七八槽的问题 时候,你会发现根本来不及搞, 本身这个SQL优化, 如果说 哥不搞索引把SQL性能 提升了 50...多倍 , 还有谁不满意???? )
算了哥详细分析一下吧。
第11步走了一个索引, 很显然是OM的 member_id 的索引。 然后第10步回表再过滤,第10步的谓词条件中,
我估摸这OM.CREATE_DT 这个条件,过滤数据最多的。 那如果哥建一个索引 OM(member_id ,CREATE_DT )
会咋样? 也就是说在不用回表就可以干掉大部分数据了。 所以就能优化SQL。
各位想想看还有其他办法再次提高性能吗??
就是回表访问的数据,放入索引中,减少回表, 那有没有其他办法再次提高性能呢??
哈,不知不觉把大家从优化SQL,带入到优化系统方面了。 根据索引 回表,就是其实就是离散IO, 而提高性能的办法也是减少离散IO的办法之一, 就是部分数据集中。( 没建索引之前,oracle是行存储的,也就是散乱的, 而我们建的组合索引, 也就是把需要的数据集中到一个标记对象中也就是索引中,)