前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >95-最近几个oracle数据库优化项目的经验总结

95-最近几个oracle数据库优化项目的经验总结

作者头像
老虎刘
发布2022-12-09 21:43:04
4710
发布2022-12-09 21:43:04
举报
文章被收录于专栏:老虎刘谈oracle性能优化

最近完成了几个比较大型的oracle数据库的优化项目, 发现一些共性问题, 写出来供大家参考.

这些项目都是远程完成的, 远程收集信息(用ora工具), 远程分析, 最后腾讯会议汇报(同时也相当于一次实战培训).

其中的一个项目, 我没敢接. 这也是到目前为止唯一一个拒绝的项目. 为什么?

现场工程师把采集到的一些性能信息发给了我, 虽然系统运行在oracle的一体机exadata上(较早版本), 但SQL的执行效率还是非常不理想. 几百G的大表, 也做了分区, 但大部分没有使用分区字段上的条件, 仍然需要全表扫描;

客户的期望是能够通过一通操作, 很快能看到明显的优化效果, 这实在是做不到. 这个系统, 我能提出一些优化建议, 但是因为设计上的原因, 需要做很多大的改动, 短期想看到效果是不可能的. 涉及到需要开发商的配合和较大的改动, 这个时间就没法控制了.

给DBA的一些建议:

很多时候, 只有系统hang才会引起dba的关注, 而系统hang大部分都是性能问题导致, DBA可能会重启数据库来临时解决, 但是如果不能从根本上解决性能问题, 这种情况还会频繁出现, 而且频率会越来越高.

  1. 参数不要乱改 很多DBA百度到了一些"专家"建议, 希望通过修改参数达到优化的目的, 实际可能起到相反的作用, 下面列举的是某些"专家"建议修改的一些参数:

_optimizer_cost_based_transformation

_optimizer_null_aware_antijoin

_optimizer_mjc_enabled

_optimizer_unnest_corr_set_subq

_optimizer_squ_bottomup

_optim_peek_user_binds

optimizer_index_caching

optimizer_index_cost_adj

......

我的参数设置原则是:

保持大部分参数为默认值, 对于存在bug的情况, 影响的是极少数特殊的SQL, 可以在sql级别单独处理 (hint或sql_patch); 只有对那些影响面比较大的参数, 才在system级别做全局调整.

(很多开发人员也期望有神奇的参数可以达到优化的效果, 可惜真没有)

2. 关于统计信息收集

不知道是出于什么原因, 有一部分数据库的自动收集统计信息被关闭.

oracle为收集统计信息做了自动任务, 这个自动任务建议是要开启的, 否则就有可能不定时的出现SQL执行计划变差导致的性能问题.

统计信息是优化器为sql生成正确执行计划的最重要依据, 统计信息不准确, 生成的执行计划就非常可能不是最优.

有些资深dba用自己的脚本收集, 不太建议; dba可以结合业务特点, 在默认收集任务的基础上做微调, 比如调整时间窗口, 与业务错开; 分区表增量收集;大表并行收集; 并发收集; 调整默认stale比例(默认10%); 调整采样比例; lock 归档历史表统计信息; 大分区表copy/set统计信息等方法.

关于手动收集统计信息:

遇到执行计划变差, 手动收集统计信息的几个重要的知识点(包括开发人员在程序代码里面调用收集统计信息命令):

使用dbms_stats.gather_table_stats命令,不要使用analyze table 收集统计信息, 因为:

analyze命令是oracle很早之前没有dbms_stats命令的时候用的,也能收集统计信息(有了dbms_stats后, analyze主要只用来做validate structure 和list chained rows), 命令写起来比较简单,但是有比较大的缺陷;

可能因为mysql数据库用的就是这个命令收集表的统计信息,也会误导一部分人在oracle数据库上也用这个命令.

dbms_stats.gather_table_stats和analyze table 具体有什么区别, 你可以自己动手做个比较, 这样会印象深刻.

还有人发现用dbms_stats.gather_table_stats收集完统计信息后, 执行计划没有马上改变. 这是因为没有使用 no_invalidate=>false参数. 这个参数, 只有那些经验丰富的dba才知道.

最后给DBA的一点补充建议:

给开发人员合理的采集性能信息权限, 比如生成awr , 查看数据字典等.

给oracle数据库开发设计人员的几个建议:

数据库性能问题, 90%以上(我个人的经验值)是开发设计上的问题, 下面列举几个常见的:

  1. 基本上绝大部分业务端没有做负载控制(并发量控制) 遇到sql执行时间长, 很多active session长时间不释放, 新的业务请求仍不断增加, 系统不堪重负, 就hang了.

需要中间件起到一个闸门的作用, 不要以为数据库有求必应, 数据库的负载是有限度的, 超出极限负荷就会出问题. 在active session达到一定的阀值后, 就不要再增加新的请求. (有时虽然应用端没有新的请求,但是数据库本身的定时任务也是没办法避免的)

2. 没有合理使用绑定变量

导致大量的SQL硬解析, 95%以上的开发人员不知道什么是绑定变量的写法; 这个百度一下, 很简单, 只是知道和不知道的关系.

如果一开始没有这个概念,等系统上线前测试或者已经上线了, 再发现问题,那改动起来就比较难了.

对于mysql/pg等其他数据库, 不使用绑定变量, 问题可能还没有那么严重, 但是对于oracle, 会带来很多问题,比如并发量上不去, ora-4031, 系统越来越慢(buffer cache被shared pool侵占), top sql不容易被发现等问题.

3. 使用模糊查询作为大表的主要过滤条件

如 instr(col_name,'xxx')>0 或者 col_name like '%xxxxx%', 这种一般需要全表扫描, 随着记录数的增加还会越来越慢; 虽然可以通过改写, 使用index fast full scan,也是提升有限; 最好还是调整业务需求, 能改成 like 'xxxxx%' 为最佳(去掉一个百分号,去掉前面那个最好, 去掉后面那个也行, 就是需要做一些特殊处理).

4. sql写法与业务逻辑的实现方法:

大结果集的分页查询,还有用分页查询的逻辑做数据导出 , 都是不建议的. 分页就尽量把结果集缩小; 导出就一次性导出;

大结果集使用标量子查询 ,执行时间会很长, 而且并行也帮不上忙. 有的需要改成外关联, 有的可能要借助物化视图的快速刷新(比如一些报表);

......

5.plsql结合上下文的写法优化:

如果不看plsql代码的上下文, 单个sql是没有问题的, 下面举几个例子:

5.1 两步可以合并为一步, 这个比较简单

原写法: delete + insert两步:

delete from xxxxxx rp

where rp.resource_id = :b1 and rp.entitytype_id = :b2;

insert into xxxxxx (georegion_id, entitytype_id, resource_id)

values (:b3, :b2, :b1);

改进写法, 直接一步update(表中其他字段置为null):

update xxxxxx set georegion_id=:b3

, other_col1=null,other_col2=null,....

where entitytype_id=:b2 and resource_id= :b1;

5.2 两步可以合并为一步, 也比较简单

原写法, 先insert , 再update:

insert into temp_xxx (aaa, bbb, ... memo)

select * From ext_xxx;

update temp_xxx na set na.aaa = 29010 where na.bbb = '111';

update temp_xxx na set na.aaa = 29011 where na.bbb = '222';

update temp_xxx na set na.aaa = 29012 where na.bbb = '333';

改进写法, 一次insert即可, 不需要update:

insert into temp_xxx (aaa, bbb, ... memo)

select decode(bbb,'111',29010,'222',29011,'333',29012,aaa) ,bbb, .....,memo From ext_xxx;

5.3 一个存储过程调用一个函数实现的逻辑, 其实可以用一个简单SQL就能实现:

--函数(sql存在没有使用绑定变量的情况, 可以增加一个rownum=1的条件):

create function func_getcnt(p_fids clob) return number is

v_count number;

v_sql varchar2(3000);

begin

v_sql := 'select count(distinct id)

from tf

where id in (' || p_fids || ')';

execute immediate v_sql

into v_count;

if v_count > 0 then

return 1;

else

return 0;

end if;

end func_getcnt;

/

--存储过程, 调用上面的函数, 计算出两个值:

create procedure proc_getjituan( p_out1 out number, p_out2 out number)

is

v_count1 number := 0;

begin

select count(distinct pid) into p_out1 from tp;

for x in (select pid,wm_concat(fid) fids from tp group by pid) loop

if func_getcnt(x.fids) = 1 then

v_count1 := v_count1 + 1;

end if;

end loop;

p_out2 := v_count1;

end;

/

SQL怎么写, 你可以动手试试, 也不难.

注: 上面几个SQL都是从客户的业务代码中脱敏简化而来.

以上观点仅为个人看法, 如有不当欢迎批评指正.

(全文完)

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档