其实是标题党,吸引你进来,来做一次历时一周的sql优化。
实际的sql为(表名,列名与实际数据为虚构,如有雷同,纯属巧合):
select * from A where data =‘28-FEB-2025’ and ID = 1 and dep_id = 11022;
背景介绍:
- 数据库:Oracle 19C
- 并不是分区表
- 表内数据大概在9亿左右
- 索引建在column date,ID,DEP_ID
- 表的统计更新状态正常
- 索引状态正常
- 当前状态的结果集数量为70万,全部返回的时间为55分钟。执行计划的cost接近20000
- 开发同事希望全部返回的时间降到10秒-20秒
面对这样的背景,我沉默了很久。和他说,咱们先把它改成分区表吧。这张表这么大,你们之前是怎么忍过来的。
在和业务的同事开会讨论了三天之后,我们成功地把这张表改为了分区表。分区键为该查询的三个条件,即date,ID,dep_id.
有进行了一天的讨论后,一直认为,并没有做复合分区的必要。
所以,目前的表的状态为
- 分区表
- 分区键为date,ID,dep_id
- 每一个partition 不存在 subpartition
- 每一个分区的数据量为百万级别(80万-130万)
- 每个分区的统计更新状态正常
- 暂时没有新建索引
- 工具执行当前查询的返回时间为0.1秒
- 返回全部结果的时间为25-30分钟
第一阶段的优化后,我当时认为已经到达极限了。无法再继续优化了。因为此时的实际sql为:
select * from A partition(P1);
这与
select * from table_A;
没有区别。
不过,我还是接受了这样的疑问,为什么不加入parallel hint?
把sql变为
select /* + parallel (16)*/ from table_A;
我说,会有作用,但并不明显。原因在于当前,工具执行的时间为0.1 秒,代表着查询的速度其实是很快的,我们现在的瓶颈在print 结果上面。如果不增加查询条件,减少返回值。速度不会改变。
当然我们还是进行了测试,分别进行了并行度 8,16,32的测试。
测试结果与不加并行几乎无差别。
压力又回到了业务组的同事,你们真的需要这么多的数据嘛?
再又一轮的讨论中,我们得知,可以新加三个查询条件。city_code, int_code 和 AR。
不过,这三列里面的distinct value并不多。单独以某一列做b tree索引,并不是一个好的办法。
目前有这样两条方案:
1.把这三列绑定在一起做组合local索引。
2.把这三列绑在一起做位图local索引。
实测下来,执行计划中的cost均有明显降低,由5500降到了400左右.
但最后我们放弃位图的原因是,如果不在查询中加入指定index的hint,有时候,数据库的优化器并不会选择这个索引。
开发组的同事表示,这对后期的代码改动以及交接业务会带来一定的工作量。既然两个差不多,就选简单的吧。
建好索引,交差。
本以为这是事情的终章了,但是在夜深人静的时候,还是觉得隐隐不安,打开电脑,果然出现了新需求。
select * from A where data =‘28-FEB-2025’ and ID = 1 and dep_id = 11022 and city_code||‘.’||int_code in (‘122.9’,‘13.11’,‘6.3’);
这条查询为啥不走索引呢?
未完待续