我做DBA这行快十年了,最怕听到的一句话就是:“昨晚那个报表跑了三个小时还没出来。”

说实话,Oracle优化这事儿,说难也难,说简单也简单。很多人一上来就想着改数据库参数、调内存配置,结果折腾半天,性能没见好,系统反倒更不稳定了。其实啊,90%的性能问题都出在SQL本身。我见过太多案例,一个烂SQL能把整个数据库拖垮,而一个写得好的SQL,查询速度能提升几十倍甚至上百倍。
今天我就把压箱底的5个优化方法拿出来,都是我在一线实战中反复验证过的。这些方法不复杂,但只要你用好了,查询性能飙升300%绝对不是吹牛。
第一个方法:学会看执行计划,别让Oracle替你瞎走。
我刚入行那会儿,有个前辈跟我说过一句话,我到现在都觉得是真理:“写SQL之前,先想想Oracle会怎么走。”很多人写完SQL就往生产上一扔,结果慢得要命,还找不到原因。其实最简单的方法就是打开执行计划。你只需要在SQL前面加一句explain plan for,然后查一下plan_table,Oracle就会告诉你它打算怎么执行这条SQL——是走全表扫描还是索引扫描,用了哪个索引,连接顺序是什么。
我见过最离谱的一个案例,某系统有个查询要跑40分钟,打开执行计划一看,Oracle傻乎乎地走了全表扫描,扫描了一张5000万行的表。原因是什么呢?就是查询条件里对索引字段用了函数,导致索引失效。改成函数索引后,40分钟变成了3秒。这就是执行计划的魅力。
第二个方法:索引不是越多越好,关键是要用对。
很多开发人员有个误区,觉得索引越多查询越快。我见过一张表上建了20多个索引,结果插入一条数据要等半分钟。为啥?因为每插入一条数据,Oracle就得维护这20多个索引,这不等于给数据库上刑吗?
索引到底该怎么建?我总结了三句话:经常出现在where条件里的字段要建索引,经常出现在order by或group by里的字段要建索引,经常用来做表连接的字段要建索引。但有一类索引千万别建——区分度低的字段,比如性别、状态这类只有两三个值的字段。这种索引建了不仅没用,还会拖慢写入速度。
还有个细节很多人不知道:联合索引要遵循最左前缀原则。比如你建了一个(a, b, c)的联合索引,那么只有查询条件从a开始才能用到这个索引。如果你的查询条件只用到b和c,这个索引就废了。我曾经帮一个客户优化,他们有个查询特别慢,原因是联合索引的字段顺序和查询条件不匹配,调整顺序后性能提升了好几倍。
第三个方法:写SQL要讲究,别让数据库做无用功。我见过最典型的低效写法就是:先把所有数据查出来,再用程序去过滤。比如有人写SQL喜欢用select ,一次查好几万条数据,传到应用层再一条条判断。这不是傻是什么?数据库最擅长的就是过滤,你非要把数据全捞出来自己过滤,等于让数据库干瞪眼,让应用程序干苦力。
正确的做法是:能用where过滤的,绝对不要放到程序里过滤。能用聚合函数算出来的,绝对不要一条条算。还有,尽量避免使用not in,因为not in通常会导致全表扫描。如果你必须用排除逻辑,建议用not exists替代。我做过测试,同样的逻辑,not exists比not in快至少5倍。
还有个容易犯的错误:在where条件里对字段做运算。比如where sal 1.1 > 100,这种写法会让索引失效。改成where sal > 100 / 1.1,索引就能用了。看似只是换个写法,性能差距可能是天壤之别。
第四个方法:合理使用分区表,让数据各归各位。
你有没有遇到过这样的场景:一张表里存了十年的数据,但你每次只查最近一个月的。结果Oracle每次都要扫描整张表,把九年前的数据也翻出来查一遍,这不是浪费吗?这时候就该用分区表了。
分区表的核心思想很简单:把大表切成小片,查询的时候只扫描需要的那一片。比如按时间分区,每个月一个分区。查询上个月的数据时,Oracle只扫描一个分区,扫描的数据量减少了90%以上。
但分区表也不是无脑用的。我见过有人把一张只有几百万行的表也分了几十个区,结果查询性能没提高,反而因为分区管理增加了开销。一般来说,单表数据量超过5000万行,或者有明显的分区键(比如时间、地区),才值得考虑分区。
还有一点:分区不是建完就完事了。要定期维护,比如清理历史分区、重建索引,否则时间长了,分区表的性能也会下降。
第五个方法:用好绑定变量,别让Oracle重复编译。
很多开发人员不知道,Oracle每执行一条SQL都会先解析一下,生成执行计划。如果同样的SQL每次只是参数不同,Oracle就得重复解析,这非常消耗CPU。绑定变量的作用就是让Oracle只解析一次,后面直接复用执行计划。
我见过一个系统,每秒要执行上千条类似的SQL,因为没有用绑定变量,Oracle的CPU使用率一直飙在90%以上。改成绑定变量后,CPU使用率直接降到了20%。这就是绑定变量的威力。
当然,绑定变量也不是万能的。对于那些数据分布极度不均匀的字段(比如某个值占了90%的数据),用绑定变量可能会让Oracle选错执行计划。这种情况,可以考虑用绑定变量窥探或者直接硬解析。
说到这儿,我想起一个真实案例。去年有个电商客户,双十一前夕系统突然变慢。我上去一看,发现他们有个订单查询SQL,因为没做好优化,每个请求都要扫全表。我按照上面5个方法逐一排查:先看执行计划,发现走了全表扫描;然后检查索引,发现缺失了一个关键索引;接着改写SQL,去掉不必要的字段;用上绑定变量。改完之后,原来要跑5秒的查询,现在50毫秒就出结果了,性能提升了100倍。客户当场就说:“早知道这么简单,我们早就自己改了。”
所以说,Oracle优化没有那么多玄学。很多时候,问题就出在最基础的几个点上。你只要把这5个方法吃透,日常工作中遇到的性能问题,80%都能搞定。下次再遇到查询慢的情况,别急着调参数、加硬件,先想想我说的这5个点,说不定半小时就能解决问题。


