前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >72-最近一次现场生产系统优化的成果与开发建议

72-最近一次现场生产系统优化的成果与开发建议

作者头像
老虎刘
发布2022-06-22 18:21:59
3750
发布2022-06-22 18:21:59
举报
文章被收录于专栏:老虎刘谈oracle性能优化

上周给南京某客户一个重要业务系统的数据库做优化,能实施的马上做了实施,优化前后性能对比非常明显,系统最为严重的IO负载过重问题基本得到解决:优化前一天的物理读是48亿次,优化后是15亿次,效果那是刚刚的,业务处理效率明显改善,磁盘的寿命也会有大幅提升。 这些还只是优化建议的一部分。

如果开发人员能把改SQL和业务逻辑的优化建议都完成,一天物理读少于5亿次应该也是完全没问题的。如果不知道优化能够带来如此巨大的提升,可能业务部门会考虑花个上百万换个闪存存储都有可能,实际上当前的普通存储对业务要求来说已经绰绰有余。

当前的性能提升主要是通过调整索引完成的,创建了11个索引。同时还做了调整数据库参数,把optimizer_index_cost_adj参数由20改回默认的100;再有通过hint+sql profile,让sql选择高效执行计划。这些事情DBA可以搞定。但是,如果SQL写的不好,就只能改代码,这个周期可能就比较长了。下面就列举其中的几种情况:

1、部分sql没有使用绑定变量,每秒硬解析次数将近200。这是一个超标很多的数值(硬解析小于10次/秒的系统,基本还算OK)。合理使用绑定变量是OLTP系统的一个最最基本的要求,但是目前还是有很多系统没有使用绑定变量,只能说目前我们国内的很多开发团队的数据库开发水平还很初级。有些公司会要求DBA要有OCP、OCM等资质才能上岗,但是对开发人员却非常宽容,只要功能实现了就算万事大吉,如果经过一些简单的培训,这种不使用绑定变量的低级问题应该不会出现。 这个客户的另一套数据库在某个时段的硬解析接近1000次/秒,CPU使用率接近100%,当DBA告知开发人员这是因为没有使用绑定变量的原因,开发人员居然振振有词的说以前没有出现类似问题,这就是典型的无知者无畏。

不使用绑定变量的一个无奈的解决办法是改数据库参数,将cursor_sharing有默认的exact改成force。这个改动非常不建议,很有可能引起其他性能问题,有很多bug与之相关。

2、这是一个非常低级而且普遍存在的写法问题,对一个日期字段做to_char:where to_char(starttime,'yyyy-mm-dd')='20200110'。这个starttime字段上,已经存在一个单字段索引,还有一个to_char(starttime,'yyyymmdd')函数索引。但是上面的sql写法,如果不改写,就只能再创建一个to_char(starttime,'yyyy-mm-dd')的函数索引。如果开发人员又写出 to_char(starttime,'yyyy/mm/dd')的sql呢?是不是还得再创建一个函数索引?这种sql的存在,说明开发团队还不知道什么是开发规范和SQL审核。

如果你是开发人员,上面两个写法都命中了你(可能有的开发人员仍不知绑定变量写法是个什么概念,建议马上百度一下),那么只能说你的数据库开发水平还有非常巨大的进步空间。

3、select xxseq.nextval from (select 1 from all_objects where rownum<=10) ; 这个sql是借助all_objects视图,生成10个sequence序列值。 这个写法在功能是没问题,但是sql频繁的执行,大量all_objects视图的调用,把这些本来对资源消耗可以忽略不计的sql,变成了top cpu SQL。 建议改成select xxseq.nextval from dual connect by level<=10;

4、select ...... from xxtab where name like '%这是一个字段内容的完整值%'; SQL(简化版)涉及的表比较大,消耗了大量的IO资源。 随便选择几个两个百分号中间的内容到表里按等值查询,都能查得到,说明两个百分号是可以去掉的,同时like 可以改成=。如果业务上线初期表只有几千或是几万条记录,这个sql消耗的资源也很少,但是随着时间的增长,表的数据量达到千万或上亿,你这样一个SQL就能把大部分IO资源都消耗掉。 凡是使用两个百分号模糊查询为主要过滤条件的,多从业务角度多考虑一下,尽量避免在大表上使用。

5、不明所以,照猫画出狗(不是虎)的分页写法(xxtab这个表很大,sql全表扫描,消耗了大量IO资源):

SELECT * FROM

( SELECT A.*, ROWNUM RN

FROM ( SELECT * FROM xxtab) A

) WHERE RN >= 1

and rownum<= 3000 and ((type = 'typename') and (state = 0));

这个分页写法不知道是从哪里学来的,错的离谱。type和state两个字段上的索引根本用不上,只能使用全表扫描的执行计划。正常的写法应该是:

SELECT A.*, ROWNUM RN

FROM

(SELECT * FROM xxtab

where rownum<= 3000 and ((type = 'typename') and (state = 0))

) A; 这种写法大表就能用上索引了,IO消耗也会大幅减少。

6、select ... from xxtab where (:b1 is null or col1=:b1) and (:b2 is null or col2=:b2) ; 这种写法,可能来源于某些搜索需求,两个输入框,不管有没有输入查询条件,都可以使用上面的sql。写法简单了,效率却差了很多:不管b1、b2是否有输入,col1和col2字段上即使有索引,也都只能选择全表扫描。 这个在业务上可以要求必须有一个输入条件,那么sql就可以根据不同的输入,生成不同的内容,比如b1有输入,b2无输入:select .... from xxtab where col1=b1; 两个都有输入,对应sql是select ... from xxtab where col1=:b1 and col2=:b2; 这样才能高效的使用索引。

7、用rownum分页写法取数:

SELECT *

FROM ( SELECT t.*, ROWNUM RN

FROM ( select t1.*,ROWNUM

from ( select *

from p

left join c on c.objectid = p.objectid

left join o on o.objectid = p.objectid

left join e on e.objectid = o.objectid

) t1

) t WHERE ROWNUM < 1500000

) WHERE RN >= 1200000;

这个分页sql的框架完全正确,满分。但是使用场景却是严重不合适的:p表7700万记录(o表也很大),4表left join没有谓词条件,主查询结果集应该也至少是7700万,每次取30万(1500000-1200000)条,要执行250多次,执行计划都是全表扫描,需要两个大表做250多次全表扫描。如果这些sql是串行执行,那么还有一个逻辑问题,就是这些表的数据都是动态变化的,用rownum分页这种方法,一定会造成漏取或重复取数。其实这个sql的最佳做法就是不加分页,一次完成。表扫描只需要一次,而且也不会出现漏取或重复取数的问题。

总结:

SQL优化对大部分系统来说,都能带来非常大的性能提升。很多问题靠更换高级硬件是无法解决的,比如一个大表全表扫描,现有硬件条件执行时间10分钟,更换硬件后执行时间1分钟。而通过优化SQL,建个合适的索引,在不更换硬件的情况下,可能用不到10毫秒就能出结果。

类似的sql写法问题可能还有很多,这次只是挑选了其中一部分top sql做分享。SQL写法灵活多变,性能好的SQL都是符合规范的,性能差的SQL会暴露你的开发水平。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老虎刘谈oracle性能优化 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档