导致性能问题的可能原因

  • 表没有正确的创建索引----错误的执行计划
  • 表没有及时的分析-------错误的执行计划
  • 热块-------数据块的争用(反向索引?)
  • 锁的阻塞------业务设计缺陷
  • SQL解析消耗大量CPU ----变量绑定
  • 低效的SQL -----SQL自身的问题
  • 数据库整体负载过程----架构设计的问题

自底向上法

TOP_DOWN_APPROACH.jpg

以下是 Oracle 公司提供的自底向上性能优化的过程:

    01.检查系统的利用率
    02.检查等待事件
    03.检查物理IO
    04.确定范围-系统级,模块级,用户级?
    05.确定最消耗资源的SQL
    + STATSPACK/AWR报告
    + Top Sessions in OEM
    06.分析执行计划 explain plan
    07.分析访问的对象(size/cardinality)
    08.分析连接、访问方式
    09.找出相关问题
    10.确定解决方案
    11.测试解决方案
    12.生产环境实施解决方案

自上向下法(尽可能从小范围分析问题)

  • SQL层

    • 如果能定位到SQL,就不要从会话层面分析
    • 工具:执行计划,10053, 10046....
  • 会话层

    • 如果能定位到会话,就不要从系统层面分析
    • V$SESSION,V$SESSTAT,V$SESSION_WAIT,V$SQL,V$LOCK,SQL_TRACE
  • 系统层

    • 如果无法定位任何性能问题,从系统层面入手
    • AWR, OS tools(top, iostat....)

环境模拟构造

  • mkdb.sql: 对数据库进行各种操作以构建环境
  • mksql.sql: 对数据库执行一些效率低下的 SQL

环境信息采集

  • spooldb.sql:对数据库进行整体信息采集,产生5个文件:数据库整体信息报告,最近7天的 ADDM 报告,最近半小时的 ASH 报告,最近1小时和7天的 AWR 报告
  • spoolsql.sql: 提示输入 sql_id,对 SQL 的执行计划及其涉及到的表、索引信息进行收集
  • interval_db_activity.sql:查询数据库分时段的活动情况/健康状态

  • 局部分析调优所需数据:SQL 的执行计划
  • 整体分析调优所需数据:主机、网络、存储、数据库(AWR, ASH, ADDM, AWRDD, AWRSQRPT)。

    • AWR 是关注数据库整体性能的报告(体检报告)。(awrrpt.sql,中间不能重启)
    • ASH 是数据库中的等待事件与哪些 SQL 具体对应的报告(胃镜)(ashrpt.sql,中间可以重启)。
    • ADDM 是 Oracle 给出的建议报告(门诊报告),一般是从数据库整体配置和局部 SQL 两方面给出建议(addmrpt.sql)。
    • AWRDD 是 Oracle 针对不同时段的性能的一个对比报告(两次体检报告的对比报告),一般 AWR 报告关注什么,AWRDD 就关注什么(awrddrpt.sql)。
    • AWRSQRPT 获取 SQL 更详细的信息,比如执行计划(活检),注意,因为执行计划可能会随着环境和数据的变化而变化,所以可能会看到一个 SQL_ID 有多个执行计划(awrsqrpt.sql,需要输入要分析的SQL_ID)。

调优手段

  • 着手调优之前,要对现有系统进行相关的了解,比如:

    • 数据库慢是一直都慢还是突然变慢,突然变慢的话最近是否有做变动?
    • 数据库是某个菜单慢还是全部模块都慢?
    • 之前用时多久?现在用时多久?你期望的时间是多久?
    • 调优 SQL 是单个大查询还是大量小查询的重复?
  • 数据库慢的时间段是什么?通过AWR分析这段时间,看看存在什么严重的等待事件,再通过ASH报告明确这些等待事件是由哪些SQL引起的
  • 删除多余的SQL,精简改写逻辑被人为复杂化的SQL是简单粗暴但非常有效的方法。在绝大部分人的脑子里,都是认为所有的应用都是必要存在的,DBA的任务就是去改进优化这些应用,而从未考虑过哪些代码功能是多余的,是绕弯路的

    • 比如,某存储过程中的一条SQL指令运行缓慢,经分析,该段代码的大致作用是判断目标表T表记录中的某列值是否有重复,如果有重复保留最早插入的一条即可,这表还有另一个字段,叫deal_time,记录插入的时间。但实际上,这段代码根本不需要优化,直接砍掉即可,因为我们只需要在该列上有唯一约束即可。
    • 再比如,某段代码的逻辑是保证 EMP 表中员工对应的部门编号必须在 DEPT 表中存,这也是多余的,直接砍掉就是,因为主从表外键关联就是干这个的。
    • 再比如,某系统 CPU 空闲率 0%,分析 top 命令发现,各进程对应的都是同一条语句,原来是有个频率为5分钟的JOB,而该SQL语句运行缓慢,要1个小时左右才结束,最终导致进程越来越多,系统越来越慢。最后经过多方面确认,该SQL是对多年前的一个临时应用补丁,将某个月份的数据取出来,做单独处理,其实现在已经不需要了,所以,直接停掉这个JOB完事
    • 再比如,一个监控系统很繁忙,CPU 空闲率很低,通过 AWR 报告发现,每个 SQL 语句的运行时间大多在 0.1 和 0.01s 即可完成(此时已经很难优化),但执行次数确很多(高达1千万次),此时,就只能从执行次数上面下手,很简单,降低监控指标的采样频率即可
    • 再比如,某项目组人员写的 SQL 语句都带有 distinct 关键字对 T 表进行处理,经确认,这个T表是外部提供的接口表,他们需要对这个T表的记录做二次提炼和开发,然后将给下一环节进行处理,而来源数据又不可控,故只能在代码中使用 distinct 去重。其实,完全可以专门针对T表再建一个去重后的中间表T1表,然后应用代码使用T1表。
    • 再比如,某计费系统在计费出帐时涉及的某个流程,插入帐务表的动作非常缓慢,每秒钟才几十条,最慢时才几条。该表有14亿条记录,但其实,插入操作按理说跟表的大小是没有关系的,但如果表上有太多索引,则会影响到插入速度,在结合等待事件一看,基本上都等待在索引的更新上面。一个改进办法就是,在跑批前禁用索引,跑批完成后再重建索引。如此,插入速度变成了每秒 4000 条,索引维护时间约1小时左右。
    • 再比如,我们在迁移一个TB级的数据库时,就要考虑一下,是否需要迁移索引,是否需要迁移大表数据(大表多是历史表,很多情况下只导表结构就可以了)等 。
    • 再比如,如果要在规定的窗口内完成数据的导出与导入,不见得我们就一定要先导出完,再做导入。我们可以将导出拆成几个大小不同的任务,在导出的同时,就可对先完成的数据进行导入了。
  • 对大表记录的保留情况进行合理规划,让大表变为小表是简单粗暴但非常有效的手段,例如表瘦身、表分区等。可以通过 user_segments.segment_taype='TABLE' 来找出现有的未分区的大表。
  • 该 SQL 返回记录有多少条?很少则调优空间较大,反之要考虑其它手段,例如表瘦身、表分区等。
  • 操作系统是否打开大页,并一定要通过 ipcs -m 和 /proc/meminfo 来确认 Oracle 内存的使用情况。
  • Oracle block 的大小如何决定?块大,存放的数据量就多,访问所产生的逻辑读就越小,但同时,因为很多数据都在同一块里,不同的人访问的数据落在同一个块的概率就大大增加,产生热点块竞争的概率就会增加。因此,在 OLTP 系统中,BLOCK 过大是不适合的,在 OLAP 系统里,BLOCK 大还是比较有用。
  • 表空间的自动扩展的确是会对性能有影响的。
  • 如果回收站中的对象过多,会导致表空间相关查询变慢,例如表空间使用率统计。
  • 是否一定要使用堆表?Oracle 针对普通堆表的缺陷,设计了其它类型的表。

    • 表太大,检索数据困难:分区表;
    • 日志开销较大,delete 较耗时,低并发:全局临时表;
    • 索引回表开销大:IOT 表(慎用);
    • 离散插入导致顺序访问比较困难:簇表(慎用)。
  • 如果待分析 SQL 涉及到全局临时表,必须要在 session 中追踪才有意义,单独提取出来执行计划是无意义的(临时表的会话独立特性)。也正是因为如此,千万不要对全局临时表收集数据,从系统层面收集统计信息,数据量一定是0,但实际情况是 session 里才知道有多少数据量,这样 oracle 就可能会采取错误的执行计划。
  • 是否做到什么值就使用什么列,否则容易发生类型转换,导致额外的开销和索引失效。另外,char 与 long 基本要被替换了。
  • 压缩表可以减少对象的大小,占用更少的 block,从而减少 IO 以提升性能。不过要注意,压缩表更新的开销会更大,查询也更消耗CPU,所以压缩技术一般适合更新少且 CPU 消耗不大,同时 IO 消耗很大的系统中。同样,索引也可以压缩,另外,联合索引的压缩度会高一些。还有要注意的事,如果压缩率不高,也没有必要去使用压缩技术。
  • 直接路径插入 /+ append / 可以提升 insert 的速度 ,但会影响接下来对新插入数据的读取,如果仅仅只是把数据插入数据库,随后并不会立即读取,可以考虑直接路径插入。另外, append 方式是直接在高水位线之上找块。
  • 如果数据不重要,不记日志可以提高性能(但要注意 DG 会强制日志的):alter table test nologging;
  • 如果能使用 nologging 关闭日志 + 直接路径写,效果更加。
  • 通过 AWR 报告可以看出数据库是否存在硬解析过多的问题(硬解析过多会导致 CPU 使用率过高以及 latch 争用),如果存在,则需要找出未绑定变量的 SQL(方法见文)。绑定变量使用示例:

    未用: execute immediate 'insert into t values ( '||i||')';
    使用: execute immediate 'insert into t values ( :x )' using i;
  • 对于很少变化的 SQL 结果集或者函数的结果集,可以考虑 result cache 技术,例如:

    首次执行:select /*+ result_cache */ count(*) from t;
    再次执行:select /*+ result_cache */ count(*) from t; 逻辑读将为0,直接从 shared pool 去拿结果缓存。
  • 将重要数据 keep 在 buffer cache 中不被挤出去也是一个常见的做法:

    SQL> alter index idx_t storage(buffer_pool keep);
    SQL> alter table t storage(buffer_pool keep);
  • 对于循环,一个常见的错误就是不批量提交(commit 在循环内),尽量使用批量提交(commit 在循环外)。找出未批量提交的语句,方法见文。
  • 检查是否存行链接与行迁移,方法见文。如果有行链接与行迁移,则设法消除,方法见文。
  • 表是否存在高水位线的问题?可以看看表中实际有多少行,然后评估一下表应该消耗多少空间(见文),实际上又消耗了多少空间,比较一下,就可以判断该表的空间使用是否合理了。另外还可以通过段顾问来来分析表空间的使用(见文)。如果存在高水位线的问题,可以 shrink, 也可以 move 以数据重组的方式来解决。
  • 如果表连接涉及的表的个数过多,可以适当地在表中增加列以空间换时间(反范式)。
  • 合理使用并行查询。并行是把双刃剑,并行是要抢占资源的。小任务实际上是不需要考虑并行的,因为并行是需要调度的,调度是需要开销的。例如,执行一条SQL平均不到0.01秒,但是并行调度的开销甚至达到了0.1秒,结果就是越跑越慢。
  • 在 OLTP 系统中(大量SQL),不绑定变量,可能会造成大量硬解析,也会导致大量的 shared pool 中的 latch 争用。尽量不要使用 cursor_sharing 参数,因为其一直 bug 不断。如果真的要用,可以设置为 force 或者 similar:n,当设置为 force 时,Oracle 强制进行绑定化处理,而不管语句的执行计划是否合理。设置为 similar 时,Oracle会判断进行绑定化处理后,其执行计划是否合理。
  • buffer cache 中的 latch 争用 --- 热块与多版本。
  • Latch优化的思路:Latch导致的性能问题,通常是一个系统层面的问题。

    • 通过AWR报告和v$latch分析latch资源情况。
    • 确定争用最大的Latch,分析可能的原因。
    • 从应用层面和数据库层面考虑解决途径。
  • 合理使用物化视图。
  • 正确使用索引,具体见文。
  • 合理使用分区表,具体见文。
  • RBO(8i之前),CBO(8i之后)。CBO 的工作模式(optimizer_mode)有两种:

    • all_rows--- 以结果集的全部处理完毕为目的。
    • first_rows(n)--- 以最快返回n行为目的。
    • 可以系统级设置或者会话级设置,也可以 SQL 级设置:/*+ all_rows */
  • CBO的核心是成本的估算:数据访问的成本(IO成本和CPU成本)和数据处理的成本(CPU成本)。
  • 检查执行计划的表连接方式是否有问题,关于执行计划和表连接,见文。
  • 统计信息的正确与否对正确执行计产生的影响至关重要,如果统计信息不正确,需要手工收集,见文。
  • RAC 下的性能问题大部分是由应用造成,主要策略是分析节点间通信,重点关注 GCS 和 GES 相关的性能指标和等待事件(RAC 的 Cache Fusion 技术将大量全表扫描数据在节点之间频繁进行通信和协调处理,加剧性能问题,引起节点间私网通信成为瓶颈)。
-- By 许望(RHCA、OCM、VCP)
最后修改:2023 年 01 月 18 日 04 : 00 PM
如果觉得我的文章对你有用,请随意赞赏