oracle性能优化
Posted wangzihong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了oracle性能优化相关的知识,希望对你有一定的参考价值。
--oracle性能优化 --性能的定位 --原则 尽可能从小范围分析问题 sql层 :能定位到sql,就不要从会话层面分析 工具 执行计划 10053、10046 会话层:从系统层面分析 v$session v$sesstat v$session_wait v$sql v$lock sql_trace 系统层 AWR os tools 高效的sql来自于对业务的理解和sql执行过程的理解 业务逻辑 - 优化器无能为力 create table mytable( id number ,value varchar2(50) ) select * from mytable for update; set autotrace on --打开执行计划查看 set autotrace off--关闭执行计划查看 set autotrace on explain --只显示查询结果和执行计划 set autotrace on statistics --只显示结果和统计信息 set autotrace traceonly --不显示查询输出,显示执行计划和统计信息 set autot traceonly explain --只显示执行计划 set autot traceonly statistics --只显示统计信息 --和前面一次的数做叠加 select t1.id,t1.value,sum(t2.value) from mytable t1 join mytable t2 on t2.id <= t1.id group by t1.id,t1.value --查看sql的执行计划 explain plan for select t1.id,t1.value,sum(t2.value) from mytable t1 join mytable t2 on t2.id <= t1.id group by t1.id,t1.value select * from table(dbms_xplan.display); --使用分析函数改写sql explain plan for select id,value,sum(value) over(order by id) from mytable; select * from table(dbms_xplan.display); sql语言本质上是集合的操作 -tablesan -index range scan -index fast scan -nested loop join -merge join -hash join ============= 性能优化 lock 么可以并发就没有锁 drop table t purge; create table t(id int primary key); --开两个窗口插入下面数据 insert into t values(1); --在第一个窗口选择是否提交或回滚,观察第二个页面变化 oracle中锁的分类 enqueues 队列类型的锁,通常和业务相关的 latches 系统资源方面的锁,比如内存结构,sql解析 锁的原则 1 只有被修改时,行才会被锁定 2 一条语句修改了一条记录,只有这条记录被锁定,oracle数据库不存在锁升级 3 当某行被修改时,它阻塞被人对它的修改 4 当一个事务修改一行时,将在这个行上加上行锁(tx),用于阻止其它事务对相同行的修改 5 读不会阻止写 例外select ...for update 6 写不会阻塞读 7 当一行被修改后,oracle通过回滚段提供给数据的一致性读 oracle中锁的类型 select type,name from v$lock_type; tm表锁:发生在insertupdatedelete 目的是保证操作能够正常进行,阻止其它人对表执行ddl操作 tx锁 事务锁(行锁)对于正在修改的数据,阻止其它会话进行修改。 select sid,type,id1,id2,lmode,request,ctime,block from v$lock where type in (‘TM‘,‘TX‘) order by 1,2; select object_name from dba_objects where object_id=13501; select sid,event from v$session_wait where sid in (22,23); select ... for update 锁定查询到的数据 tm锁的几种模式 lock mode 模式 锁定的sql 排斥的模式 允许的sql 2 lock table t in row share mode; 6 select insert update delete for update 3 lock table t in row exclusive mode;4,5,6 select /insert/.... 4 lock table t in share mode; 3,5,6 select 5 lock table t in share row exclusive mode; 3,4,5,6 select 6 lock table t in exclusive mode; 2,3,4,5,6 select 为什么需要手工锁定表: ================= 05 基于引用关系的锁定 RI锁 create table p(id int primary key); create table c(id references p(id)); select sid,type,id1,id2,lmode,request,block from v$lock where type in (‘TM‘,‘TX‘) order by 1,2; select object_name from dba_objects where object_id in (13504,13506); 插入数据时在从表上面加表级锁 BI锁和外键索引 死锁:两个会话互相持有对方的资源 会话1: create table t(id int primary key); insert into t values(1); 会话2: insert into t values(2); insert into t values(1); --此时出现阻塞等待 会话1: insert into t values(2); 会话1出现阻塞等待,会话2报错 ORA-00060: 等待资源时检测到死锁 ==================== 06 latch门闩 latch的目的 保证资源的串行访问 -保证sga的资源访问 -保护内存的分配 保证执行的串行化 -保护关键资源的串行执行 -防止内存结构损坏 latch --sga 资源的请求和分配 共享池 -sql解析 sql重用 数据缓冲池 -数据访问、数据写入磁盘、数据读入内存 -修改数据块 -数据段扩展 oracle有哪些latch select * from v$latchname; latch的获取 wait方式--如果无法获取请求的latch -spin 当一个会话无法获得需要的latch时,会继续使用cpu,达到一个间隔后,再次尝试,达到最大重试次数 -sleep 当一个会话无法获得需要的latch时,会等待一段时间 no wait方式-如果无法获取请求的latch - 不会发生sleep或者spin - 转而去获取其它可用的latch shared pool里的latch争用-绑定变量 declare l_cnt number; begin for i in 1..10000 loop execute immediate ‘select count(*) from t where x =‘||i into l_cnt; end loop; end; / declare l_cnt number; begin for i in 1..10000 loop select count(*) into l_cnt from t where x = i; end loop; end; buffer cache 数据区 desc x$bh 数据块头 会话访问数据块 step1 latch step2 hash bucket step3 数据块 步骤: 1 hash the block address 2 get bucket latch 3 look for header 4 found read block in cache 5 not found read block off disk data buffer中的latch争用 -热块 表数据块争用 热块索引数据热块 文件头数据块-并发修改 latch相关的视图 - v$latch v$latch 每个latch的统计信息的一个汇总,每一条记录表示一种latch select name,gets,misses,sleeps from v$latch where name like ‘cache%‘; misses:请求不成功次数 sleeps:成功获取前sleeping次数 immediate_gets:以immediate模式latch请求数 immediate_misses:以immediate模式请求失败次数 v$latchholder 包含了当前latch持有者的信息 通过视图中的pid和sid信息 v$latch_children 存储latch信息的视图 AWR报告中的latch部分 latch优化思路 AWR报告 通过动态视图v$latch 分析当前latch资源情况 确定争用最大的latch 分析可能的原因 应用层面和数据库层面考虑 ========= 08 优化器和执行计划 执行计划 sql语句访问和处理数据的方式 执行计划 数据的访问 直接表的访问 -并行 -多数据块 通过索引访问 -index unique scan 索引唯一扫描 -index range scan 索引范围扫描 -index full scan 索引全部扫描 -index fast full scan 索引快速全部扫描 -index skip scan 索引切块扫描 数据的处理 order by group by 数据的关联处理 -nested loop join 嵌套循环连接 小表有索引 -merge join 先排序 -hash join 大表和小表关联 通过表访问数据 --索引唯一扫描 SQL> explain plan for 2 select * from t where id=2; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 2719494768 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 26 | 2 (0)| | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 26 | 2 (0)| |* 2 | INDEX UNIQUE SCAN | SYS_C003776 | 1 | | 1 (0)| -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID"=2) 14 rows selected --索引范围扫描 SQL> explain plan for 2 select * from t where id>5; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 1744232805 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 26 | 1 (0)| | 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 26 | 1 (0)| |* 2 | INDEX RANGE SCAN | SYS_C003776 | 1 | | 1 (0)| -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("ID">5) Note ----- - dynamic sampling used for this statement (level=2) 18 rows selected --索引快速扫描 SQL> explain plan for 2 select count(*) from t; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 2375626531 ----------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | ----------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | INDEX FAST FULL SCAN| SYS_C003776 | 3 | 2 (0)| 00:00:01 | ----------------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) 13 rows selected --整个索引的扫描 SQL> explain plan for 2 select id from t order by id; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 3503263006 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3 | 39 | 2 (0)| 00:00:01 | | 1 | INDEX FULL SCAN | SYS_C003776 | 3 | 39 | 2 (0)| 00:00:01 | -------------------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) 12 rows selected --索引跳跃扫描 数据集的关联 --hash join --先把小表hash到内存中,然后过来关联 SQL> explain plan for 2 select t.*,t2.* from t,t2 where t.id = t2.id; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 106979157 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 260 | 7 (15)| 00:00:01 | |* 1 | HASH JOIN | | 5 | 260 | 7 (15)| 00:00:01 | | 2 | TABLE ACCESS FULL| T | 5 | 130 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| T2 | 5 | 130 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("T"."ID"="T2"."ID") Note ----- - dynamic sampling used for this statement (level=2) 19 rows selected --nested loops --从其中的一张表拿数据到另外一张表中去匹配 --sort merge join --并行 ================== 09 oracle的优化器 cbo 依据一套数据模型,计算数据访问和处理的成本,择最优成本为执行方案 CBO的工作模式 all_rows 以结果集的全部处理完毕为目的 first_rows(n) 以最快返回n行为目的 分页 优化器模式的设置方式 参数设置 optimizer_mode 会话设置 alter session set optimizer_mode=all_rows; sql设置 select /*+ all_rows */ count(*) from t; cost 代价 选择性 selectivity - user_tab_col_statistics 索引的选择性 - user_indexes cardinality 集的市 在执行计划中每一步操作返回的记录数 CBO通过这个值的权重计算,决定使用哪一种方式访问数据 10g后 rows 评估出来的数 exec dbms_stats.gather_table_stats(user,‘t‘,method_opt=>‘for all columns size 1‘); 不做直方图 索引 clustering factor 簇 集群因子 集群因子 索引代价的评估 出现一次数据块的跳转 集群因子 +1 CBO的核心 成本的计算 数据访问的成本的估算 I/O成本的估算 全表扫描 索引(单数据块 多数据块) CPU成本的估算 数据处理的成本 CPU成本的估算 ============== 10 hints oracle的hints hints是用来约束优化器行为的一种技术 优化器模式 all_rows first_rows 数据访问路径 基于表的数据访问 基于索引的数据访问 表关联的方式 NL MJ HJ hints的使用范畴 -尽量避免在开发中使用 -辅助dba用来做性能排查 访问路径相关的hints /* full */全表扫描 --全表扫描可以多块读的方式 --全表扫描可以用并行 SQL> explain plan for 2 select /* full(t) */ * from t where id < 8; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 1601196873 -------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 3 | 78 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| T | 3 | 78 | 3 (0)| 00:00:01 | -------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("ID"<8) Note ----- - dynamic sampling used for this statement (level=2) 17 rows selected --index no_index /* index(t id) */ 使用索引 /* no_index(t id) */ 不适用索引 /* index_ffs */ 与全表扫描机制相似 索引快速全部扫描 /* + index_ss */ index skip scan -用于替代全表扫描的一种数据访问方法 -对于前导重复率高的联合索引 index skip scan性能好一些 =============== 11 hints /*+ use_nl */ nested loop joins --两个结果集的关联 --内部表外部表 从一张表取数据与另外一张表取 --nl的场景 --关联中有一个表比较小 --被关联表的关联字段上有索引 --索引的键值不应该重复率高 --小表作为外部表 小表去大表进行关联 SQL> explain plan for 2 select /*+ use_nl(t,t2) */ t.* 3 from t,t2 4 where t.id = t2.id ; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 125942274 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU) -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 195 | 8 (0) | 1 | NESTED LOOPS | | | | | 2 | NESTED LOOPS | | 5 | 195 | 8 (0) | 3 | TABLE ACCESS FULL | T2 | 5 | 65 | 3 (0) |* 4 | INDEX UNIQUE SCAN | SYS_C003776 | 1 | | 0 (0) | 5 | TABLE ACCESS BY INDEX ROWID| T | 1 | 26 | 1 (0) -------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("T"."ID"="T2"."ID") Note ----- PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- - dynamic sampling used for this statement (level=2) 21 rows selected /*+ use_hash */ hash join --原理:oracle把小的表数据集hash到内存中 hash成多个分区 内存放不下放到临时表空间 --大的表把关联的字段hash 大的hash匹配小的的hash --hash join的应用场景 --一个大表,一个小表关联 --表上没有索引 --返回结果集比较大 SQL> explain plan for 2 select /*+ use_hash(t,t2) */ t.* 3 from t,t2 4 where t.id = t2.id ; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 106979157 --------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 195 | 7 (15)| 00:00:01 | |* 1 | HASH JOIN | | 5 | 195 | 7 (15)| 00:00:01 | | 2 | TABLE ACCESS FULL| T | 5 | 130 | 3 (0)| 00:00:01 | | 3 | TABLE ACCESS FULL| T2 | 5 | 65 | 3 (0)| 00:00:01 | --------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("T"."ID"="T2"."ID") Note ----- - dynamic sampling used for this statement (level=2) 19 rows selected /*+ usr_merge */ sort merge join --两个结果集先排序再进行关联 --应用场景 --当结果集已经排序 SQL> explain plan for 2 select /*+ use_merge(t,t2) */ t.* 3 from t,t2 4 where t.id = t2.id ; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 1077318368 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 195 | 8 (25)| 00:00:01 | | 1 | MERGE JOIN | | 5 | 195 | 8 (25)| 00:00:01 | | 2 | SORT JOIN | | 5 | 130 | 4 (25)| 00:00:01 | | 3 | TABLE ACCESS FULL| T | 5 | 130 | 3 (0)| 00:00:01 | |* 4 | SORT JOIN | | 5 | 65 | 4 (25)| 00:00:01 | | 5 | TABLE ACCESS FULL| T2 | 5 | 65 | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("T"."ID"="T2"."ID") filter("T"."ID"="T2"."ID") Note PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- ----- - dynamic sampling used for this statement (level=2) 22 rows selected /*+ leading() */ 规定表连接的顺序 --驱动表 被驱动表 不加是由优化器决定 SQL> explain plan for 2 select /*+ leading(t,t1,t2) */ t.* 3 from t,t1,t2 4 where t.id = t2.id 5 and t.id = t1.id; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 2090431216 ---------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ---------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 260 | 10 (10)| 00:00:01 | |* 1 | HASH JOIN | | 5 | 260 | 10 (10)| 00:00:01 | |* 2 | HASH JOIN | | 5 | 195 | 7 (15)| 00:00:01 | | 3 | TABLE ACCESS FULL| T | 5 | 130 | 3 (0)| 00:00:01 | | 4 | TABLE ACCESS FULL| T1 | 5 | 65 | 3 (0)| 00:00:01 | | 5 | TABLE ACCESS FULL | T2 | 5 | 65 | 3 (0)| 00:00:01 | ---------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - access("T"."ID"="T2"."ID") 2 - access("T"."ID"="T1"."ID") Note PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- ----- - dynamic sampling used for this statement (level=2) 22 rows selected /*+ append */ 以直接加载的方式插入数据 --传统加载:在高水位下有空的加载数据 --直接加载:append在高水位上加载,然后移动高水位 --append有时候会减少redo --当数据是归档,产生日志 产生redo --数据归档 不产生日志 不产生redo --非归档 都不产生redo alter table t nologging; --设置为不产生日志 insert into t select * from t; insert /*+ appedn */ into t select * from t; --比较两个产生的redo --设置动态采样的级别 /* dynamic sampling */ --采样率越高越准确,消耗的资源越多 select /*+ dynamic sampling(4) */ count(*) from t; --指定并行度 /*+ parallel */ SQL> explain plan for 2 select /*+ parallel(t 2) */ count(*) from t; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 3126468333 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Cost (%CPU)| Time | T -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | | | | 2 | PX COORDINATOR | | | | | | 3 | PX SEND QC (RANDOM) | :TQ10000 | 1 | | | Q1, | 4 | SORT AGGREGATE | | 1 | | | Q1, | 5 | PX BLOCK ITERATOR | | 5 | 2 (0)| 00:00:01 | Q1, | 6 | TABLE ACCESS FULL| T | 5 | 2 (0)| 00:00:01 | Q1, -------------------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) 17 rows selected /*+ driving_site() */ --决定一个分布式事务中,操作在那个节点上完成 select /*+ driving_site(departments) */ * from employees,departments@rsite where employees.department_id = departments.department_id; --如果没有hint,远程表上的数据将被传到本地来做关联 --使用hint,本地的数据将传到远程节点上执行,最后将结果返回本地 /*+ cardinality() */ --模拟一个结果集的cardinality --t表返回100条数据 select /*+ cardinality(t 100) */ * from t,t1 where t.object_id = t1.object_id and t.object_id > 100 ========== 13 等待事件 sql*net message from client --oracle一个通信协议 --空闲等待 --客户端和数据端建立好了连接,等待客户端发送指令 SQL> select distinct sid from v$mystat; SID ---------- 26 SQL> select sid,event,state from v$session where sid=26; SID EVENT STATE ---------- ---------------------------------------------------------------- ------------------- 26 SQL*Net message from client 造成oracle的等待,三种情况 请求的资源太忙,需要等待资源释放 会话处于空闲状态,等待新的任务 会话被阻塞,需要等待阻塞解除 1.数据库处理数据,只有有时间消耗,就会有等待事件 2.性能和等待是一个矛盾体 3.理解出现某种等待时间的原因 4.结合业务,主观的看待等待事件 -制定基线(baseline)发现异常等待事件 -接受合理的等待事件 等待的定位方式 sql级别 10046 event 会话级别 动态视图 v$session_wait begin for i in 1..1000 loop insert into t select * from dba_objects; end loop; end; / select sid,enent from v$session_wait where sid = 22; 系统级别 AWR报告 等待事件的分类 查看等待事件 v$session_wait 会话级别 SQL> select event,p1,p1text,p2,p2text,p3,p3text from v$session_wait where sid=26; EVENT P1 P1TEXT P2 P2TEXT P3 P3TEXT ---------------------------------------------------------------- ---------- ---------------------------------------------------------------- ---------- ---------------------------------------------------------------- ---------- ---------------------------------------------------------------- SQL*Net message from client 1413697536 driver id p1 文件id p2 数据块号 p3 读取的数据块号 常见的等待事件 ilde wait events 进程无事可做,等待分配任务 空等待意味着空闲,还意味着其它 CPU不属于等待事件 select name,value from v$sysstat where name like ‘%CPU%‘; db file scattered read 数据多块读 当数据块以multiblock read 的行式被读取到sga中时 -FTS full table scan -IFFS index fast full scan -db_file_multiblock_read_count 每次读取多少块的参数 解决: -无需解决 -考虑索引 -考虑并行 db file sequential read 顺序的读 当把数据块读入aga时,发生db file sequential等待 单数据块的读,通常指索引的读取 有些索引读取会发生db file scattered read 等待 有时候表的读取 有很多extend 边界 undo的读取 一个块一个块的读 解决: -无需解决 -sql语句的效率 -考虑其它方式的索引 复合索引 位图索引 全文索引 -全表扫描+并行 -改善磁盘I/O direct path read 直接路径数据读 排序数据内存不足,被写到磁盘上,重新读取 并行操作的slave进程的数据读取 其它的属于某个会话私有数据的读取 参数说明: p1 p2 p3 解决: 增大内存排序区 pga 调整并行度 改善磁盘I/O direct path write 同上 log file sync log文件的同步 用户commit(rollback)时,lgwr需要将log buffer的数据写道log file上面,发生等待 解决: -减少commit的频率 错误的频繁提交 -提高I/O性能 buffer busy waits 数据块繁忙等待 内存中对相同的数据块有多个并发请求,导致这个等待 参数说明: p1 读取数据快所在的文件id p2 读取的数据块id p3 等待类型 class id 解决: -热块 segment header -ASSM 自动断管理 data block -ASSM 反向索引 undo header -automatic undo management undo block -增大回滚段 free buffer waits 申请空闲的buffer -系统I/O成为瓶颈 或者性能不够 -等待资源latch争用 -sga太小 -sga太大,dbwr无法快速的把脏数据刷到磁盘上 优化I/O -提高I/O通道的性能 -异步I/O -增加多个dbwr进程 增大sga 相关的视图 v$session_event 记录会话所有等待信息 v$system_event 实例级别的视图 ============ 15 索引与分区 索引的目的 提高数据访问的效率 比较索引和直接读 consistent gets 一次性读 oracle的索引类型 B-tree索引 B树索引 根 - 枝 - 叶 高效场景 -索引字段有着很高的selectivity或者结果集很小的时候 低效场景 -索引字段有着很低的selectivity或者结果集很大的时候 基本适用所有类型的数据库 没有明显的缺点 Bitmap索引 位图索引 每条记录记录一个状态 位运算 不占空间 位图索引的运算就是计算机的位运算 场景 -OLAP -重复率高的键值 -and or 运算 不适用的场景 -OLTP -DML频繁操作 位图索引的锁定 更新一条记录会锁定另外的记录 更新同一个键值 DML操作会影响位图段 在有bitmap的表中修改数据,会对所有受影响的键值关联的记录做锁定 TEXT index 全文索引 又叫模糊查询 create index idx_t on(object_name) indextype is ctxsys.context select count(*) from t where contains(object_name,‘TABLE‘)>0; 全文索引是如何存储数据的 -查看索引 select table_name,index_name from user_indexes; select segment_name from user_segments where segment_name=‘IDX_T‘; -并不占用空间 使用场景 -b-tree,bitmap无法发挥作用的场景 -模糊查询 like ‘%string%‘ 缺点 -占用过大的磁盘空间 -维护成本高 -bug多 查看表大小 select bytes/1024/1024 from user_segments where segment_name=‘T‘; ================== 16 分区 分区表 -表分区后,分区变成各自的段,表是一个逻辑名称 -分区剪裁 表属于一个段 段属于一个表空间 -性能 数据管理 非分区表:扫描所有 分区表:范围扫描 分区的分类 list分区 -区域 地址 part_date range 范围分区 -时间 月 hash 哈希分区 组合分区(子分区) 先按列表分区再按范围分区 -range-range -list-range -list-hash -list-list 分区索引 local index -表的DML操作无需rebuild索引 -可以非常方便的管理数据 全局索引 -表的DDL操作会导致索引无效 误区:分区索引性能好于全局索引 create index idx_t on t(id); --全局索引 create index idx_t on t(id) local; 总结: 1.分区索引的目的在于数据的管理而非性能 2.一个分区表上如果经常有DDL操作,将会导致全局索引失效,需要对索引重建,此时创建分区索引更加合适 =========== 17 数据分析 CBO的数据来源 -CBO是一个数学模型 -需要准确的传入数据 -通过精确的数据计算出精确的执行计划 当没有分析数据时 create table t as select * from dba_objects; select extents,blocks from user_segments where segment_name=‘T‘; select num_rows,blocks from user_tables where table_naem=‘T‘; 没有分析数据时会自动进行动态采样 select /*+ dynamic_sampling(t 0) */ count(*) from t; -将动态采样设为0 分析信息部充足 update t set object_id=1 where object_id<1001; exec dbms_status.gather_table_stats(user,‘t‘,method_opt=>‘for all columns size 1‘); --不收集直方图信息 select * from t where object_id=1; 充足数据 exec dbms_status.gather_table_stats(user,‘t‘,method_opt=>‘for all columns size 256‘); CBO的数据来源 初始化参数 -优化参数 -CPU -数据块大小 -多块读的大小 数据字典 -user_tables,user_tab_partitions -user_indexes,user_ind_partitions -user_tab_col_statistics DBMS_STATS包和analyze命令 analyze命令已经过时 -无法提供灵活的分析选项 -无法提供并行的分析 -无法对分析数据进行管理 DBMS_STATS -专门为CBO提供信息来源 -可以进行数据分析的多种组合 -可以对分区进行分析 -可以进行分析数据管理 oracle的自动信息收集 -oracle11g的一个默认设置 -user_tab_modification跟踪表的修改 -当分析对象的数据修改超过10%时,oracle会重新分析 -定时任务GATHER_STATS_JOB负责重新收集过旧数据的信息 exec DBMS_STATS.FLUSH_DATABASE_MONITORING_INFO; --刷新 select inserts,updates,deletes,timestamp from user_tab_modifcations where table_name=‘T‘; --查看状态 select log_id,job_name,status,log_date from dba_scheduler_job_run_details where job_name = ‘GATHER_STATS_JOB‘; 是否完全依赖自动分析 1.当数据执行计划保持不错的时候,可以依赖自动分析 -OLTP系统 2.需要手工介入 -OLAP系统 3.没有一个适合所有系统的数据分析方法 手工分析 DBMS_STATS包 -Gathering_optimezer_statistics 收集 -setting_or_getting_statistics 自己设置参数 -deleting_statistics 删除 -transferring_statistics 传输 -locking_or_unlocking_statistics 锁定 表数据的收集 DBMS_STATS.GATHER_TABLE_STATS( ownname ,tabname ,partname ,estimate_percent 采样 ,block_sample 块的采样 ,method_opt default ‘for all columns size‘ ,degree 并行 ,granularity 粒度 ,cascade 索引 ,stattab 建表存储统计信息 ,statid ,statown ,no_invalidate 验证有效性 ) procedure gather_table_stats (ownname varchar2, tabname varchar2, partname varchar2 default null, estimate_percent number default DEFAULT_ESTIMATE_PERCENT, block_sample boolean default FALSE, method_opt varchar2 default DEFAULT_METHOD_OPT, degree number default to_degree_type(get_param(‘DEGREE‘)), granularity varchar2 default DEFAULT_GRANULARITY, cascade boolean default DEFAULT_CASCADE, stattab varchar2 default null, statid varchar2 default null, statown varchar2 default null, no_invalidate boolean default to_no_invalidate_type(get_param(‘NO_INVALIDATE‘)), stattype varchar2 default ‘DATA‘, force boolean default FALSE); 索引数据的收集 DBMS_STATS.GATHER_INDEX_STATS (ownname varchar2, indname varchar2, partname varchar2 default null, estimate_percent number default DEFAULT_ESTIMATE_PERCENT, stattab varchar2 default null, statid varchar2 default null, statown varchar2 default null, degree number default to_degree_type(get_param(‘DEGREE‘)), granularity varchar2 default DEFAULT_GRANULARITY, no_invalidate boolean default to_no_invalidate_type(get_param(‘NO_INVALIDATE‘)), stattype varchar2 default ‘DATA‘, force boolean default FALSE); 数据分析示例: exec dbms_stats.gather_table_stats(user,‘t‘); --表分析 exec dbms_stats.gather_index_stats(user,‘idx_t‘); --索引分析 --对表分析带对索引分析 exec dbms_stats.gather_table_stats(user,‘t‘,cascade=true); 重要的参数: estimate_percent 采样百分比 -DBMS_STATS.AUTO_SAMPLE_SIZE -手工设置(范围:0.000001,100) 1.超大表 0.000001 2.大表 3.小表 granularity 数据分析的力度 在什么层面上 针对分区表 -global -partition -subpartition 示例: create table t(id int,name varchar2(1000)) parttion by range(id) (partition p1 values less than(10000), partition p1 values less than(10000), partition p1 values less than(10000), partition pmax values less than(maxvalue)) exec dbms_stats.gather_table_stats(user,‘t‘); select partition_name,blocks,num_rows,golbal_stats from user_tab_partitions where table_name=‘T‘; insert into t select object_id,object_name from dba_objects; exec dbms_stats.gather_table_stats(user,‘t‘,granularity=>‘partition‘); select partition_name,blocks,num_rows,golbal_stats from user_tab_partitions where table_name=‘T‘; select count(*) from t where id<10000; --分区统计信息 select count(*) from t where id<30000; --全局统计信息 全局信息没有收集 select count(*) from t where id<10001; --跨分区 使用全局统计信息 全局分析和分区分析 当表上已经有全局统计信息时,单独对分区分析,不会更新全局信息 分区的合并 ============= 18 全局分析和分区分析 分区和全局信息 增量统计(oracle11g) -oracle会增量的收集分区信息来更新全局信息 -配置 exec dbms_stats.set_table_prefs(user,‘t‘,‘INCREMENTAL‘,‘TRUE‘); 如何设置这个参数 -在一个很大的分区表(OLAP),全局分析代价非常昂贵 -OLAP系统下,除了新加入的数据,旧的数据基本上没有变化,全局分析乱费资源 -对于很大的分区表 incremental(oracle11g) -对于不大的分区表,可以使用默认设置 参数: method_opt 分析直方图选项 直方图: oracle对列上的数据分布进行统计分析,对数据倾斜分布时有用 Frequency -频率直方图 -字段上键值重复率高 height-balanced 高度平衡直方图 -把数据装入每个桶中,量一样,值不一样 -装入的数据量相同(数据条数),键值不同 -size 12 相当于设置多少个桶 最大254 例: create table t as select object_id col1,trunc(rownum/1000) col2 from dba_objects where rownum<10000; exec dbms_stats.gather_table_stats(user,‘t‘,method_opt=>‘for all columus size 12‘); -- select colume_name,num_distinct,num_buckets,histogram from user_tab_col_statistics where table_name=‘T‘ and column_name=‘COL1‘ select endpoint_number,endpoint_value from user_tab_histograms where table_name=‘T‘ and column_name=‘COL1‘ 设置: 1.for all columns 统计所有列的histograms 2.for all indexed columns 统计所有indexed列的histograms 3.for all hidden columns 统计你看不到列的histograms 4.for columns <list> size <n> |repeat|auto|skewonly: -N的取值范围[1,254] -repeat 上次统计过的histograms -auto 由oracle决定N的大小 -skewonly 只收集非均匀分布的直方图,系统决定桶数 列的相关性 扩张的统计 extended statistics select count(*) from sh.customers where cust_stats_province=‘CA‘; and country_id=52775 --实际结果与执行计划差别较大 country_id=52775 country_id=52790 EXEC DBMS_STATS.GATHER_TABLE_STATS( ‘SH‘ ,‘CUSTOMERS‘ ,METHOD_OPT=>‘FOR ALL COLUMNS SIZE SKEWONLY FOR COLUMNS (CUST_STATE_PROVINCE,CONUTRY_ID) SIZE SKEWONLY‘ --列的相关性分析 ) 动态采样 当表上没有分析信息时,oracle会使用动态采样技术 发生在sql解析时(硬分析)有限的数据块 不同级别,采样的数据块数量不同 -level 1-10 采样数据量逐级递增 -level10 对所有数据进行采样分析 1.动态采样只能作为一种辅助手段 2.海量数据,动态采样的数据块太少,无法准确的反映数据的真实情况 3.采样率高,会影响sql的执行效率 4.dbms_stats可以非常灵活的进行数据分析配置 -分析比例 -分析时间 -直方图 -分析数据的管理 ============== 19 并行 将一件工作分成很多块,分别由不同的进程来执行,最后将结果合并 应用场景: 1.OLAP数据仓库 -OLAP是一个业务模型 -数据仓库 用于支撑的数据库 2.整块的数据读取操作 -FTS 全表扫描 -IFFS 3.并行执行高效的要素 -充足的系统资源 -待处理的数据分布均匀 并行的机制 用户连接到数据库 - 启动相应的server process - 用户发出并行server process变成并行协调进程(qc) select /*+ parallel(c,2) */ * from customers order by cust_last_name,cust_first_name 每步启动两个进程 并行的执行计划 SQL> explain plan for 2 select /*+ parallel */ * 3 from t 4 order by id; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------- Plan hash value: 3122197462 -------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time -------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 5 | 130 | 3 (34)| 00:00: | 1 | PX COORDINATOR | | | | | | 2 | PX SEND QC (ORDER) | :TQ10001 | 5 | 130 | 3 (34)| 00:00: | 3 | SORT ORDER BY | | 5 | 130 | 3 (34)| 00:00: | 4 | PX RECEIVE | | 5 | 130 | 2 (0)| 00:00: | 5 | PX SEND RANGE | :TQ10000 | 5 | 130 | 2 (0)| 00:00: | 6 | PX BLOCK ITERATOR | | 5 | 130 | 2 (0)| 00:00: | 7 | TABLE ACCESS FULL| T | 5 | 130 | 2 (0)| 00:00: -------------------------------------------------------------------------------- Note ----- - dynamic sampling used for this statement (level=2) - automatic DOP: Computed Degree of Parallelism is 2 19 rows selected =============== 20 并行与性能 并行度,oracle进行并行处理时,会启动几个并行进程来同时执行 并行度的设定 -elaspend time -system cpu time -system wait cpu time 获得sql的并行度 v$px_session.degree v$pq_tqstat 10391事件 并行相关的初始化参数 parallel_degree_limit parallel_degree_policy parallel_force_local parallel_min_time_threshold parallel_servers_target parallel_execution_message_size parallel_degree_policy -manual(default) -limited -auto 并行用途 并行DDL操作 -create table ...as select -alter table move partition 并行DML(分区表) -update -delete -merge =========== 21 变量绑定 select * from t where id=:B; begin for i in 1..4 loop execute immediate ‘select * from t where rn=:i‘ using i; end loop; end; 变量绑定的目的 1.减少sql硬解析的次数 2.减少系统资源开销 3.减少latch争用 应用场景: 1.适用于OLTP -用户并发很高 -表中有主键 -操作的数据少 -执行计划基本相同 -sql重复率高 2.不适用OLAP -执行计划多变 -用户少 -sql解析对系统性能影响小 sql语句的处理 硬分析(hard parse) -语法解析 -语义分析 表是否存在 -shared pool chech 当sql语句第一次执行时,都会被硬解析 软解析(soft parse) -共享池中搜索执行计划 -使用现有执行计划执行sql alter system flush shared_pool; --刷新shared_pool 会话对游标的缓存(softer_soft_parse) session_cached_cursor -对于已经关闭的cursor,可以把它的资源保留在pga中,后续对cursor继续调用 -如果该参数设置为0,oracle将会对关闭的游标重新打开 父子游标 同样的sql,会产生另外的cursor -父游标parent cursor 第一条运行的sql -子游标 child cursor 后续的sql select sql_id,child_number,sql_text from v$sql where sql_text=‘select * from emp where 1=0‘; select sql_id,auth_check_mismatch from v$sql_shared_cursor where sql_id = ‘‘; ============ 22 游标及共享 游标共享 cursor_sharing alter session set cursor_sharing=EXACT; --共享游标设置 cursor_sharing=force --强制绑定 cursor_sharing=similar --有不同的执行计划 生成两个游标 bind peeking 变量窥视 oracle在第一次解析sql时,如果sql上有变量绑定,会查看这个变量的值,准确的指定执行计划; 后续的分析中,将不会理会这个变量的值 使用场景 -执行计划机会不改变 -大量的并发 -大量的除谓词外机会相同的sql 不适用的场景 -执行计划会随变量值的变化而变化 -少量的sql adaptive cursor sharing(ACS) 通过不断观察bind的值,来决定新的sql是否使用之前的执行计划,解决绑定变量导致后续执行计划不变的问题 缺点: -更多的硬分析 -产生更多的子游标,需要更多的内存 -消耗更多的CPU oracle使用ACS的前提条件: 绑定变量使用bind peeking 绑定变量的列上有直方图 清理两次共享池?oracle11g =============== 23 sqltrace 用以描述sql的执行过程的trace输出 -sql是如何操作数据的 -sql执行过程中产生了那些等待时间 -sql执行中消耗了多少资源 -sql的实际执行计划 -sql产生的递归语句 set auto trace(explain plan) -输出优化器的产生的执行计划(估算值) sql_trace sql的实际执行情况 -消耗的资源 -产生的等待事件 -数据的处理过程 ... 当需要分析执行计划及CBO行为时,使用 set auto trace(explain plan) 当要看一条sql的真实运行效果时,使用 sql_trace(10046) 产生一个sql_trace alter session set sql_trace=true; select count(*) from t; alter session set sql_trace=false; select * from v$diag_info where name like ‘Default%‘; 阅读原始的trace文件 parsing in cursor部分 len -长度 dep -递归深度 uid -user id otc -oracle command type 命令的类型 lid -私有的用户id tim -时间戳 hv -hash value ad -sql adress parse,exec,fetch部分 c -消耗的cpu time e -elspsed time 操作的用时 p -physical reads 物理读次数 cr -consistent reads 一致性方式读取的数据块 cu -current 方式读取的数据块 mis -cursor miss in cache 硬分析次数 r -rows 处理的行数 dep -depth 递归sql的深度 og -optimizer goal 优化器模式 tim -timstamp 时间戳 stats部分 id -执行计划的行源号 cnt -当前行源返回的行数 pid -当前行源号的父号 pos -执行计划中的位置 obj -当前操作的对象id op -当前行源的数据访问操作 tkprof -格式化trace文件的工具 tkprof /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_4271.trc out.txt --去除递归 tkprof /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_4271.trc nosys.out sys=no --添加优化器执行计划 tkprof /u01/app/oracle/diag/rdbms/orcl/orcl/trace/orcl_ora_4271.trc exp.out explain=test/test; tkprof报告中的数据块的两种读取方式 ============ 24 trace事件和10046事件 跟踪其它会话的sql grant execute on dbms_system on test; select distinct sid from v$mystat; select sid,serial# from v$session where sid=27; execute sys.dbms_system.set_sql_trace_in_session(27,60,TRUE); --描述性语言 select /*+ trace_by_other_session */ count(*) from t; execute sys.dbms_system.set_sql_trace_in_session(27,60,false); 第三方软件 -toad -pl/sql developer 10046 event alter session set events ‘10046 trace name context forever.level n‘; -level 1 等同于sql_trace的功能 -level 4 在level 1的基础上增加收集绑定变量的信息 -level 8 在level 1的基础上增加等待事件的信息 -level 12 等同于level 4+level 12,同时收集绑定变量和等待时间信息 variable x number; exec :x:=1; alter session set events ‘10046 trace name context forever.level 1‘; select count(*) from t where object_id = :x; alter session set events ‘10046 trace name context off‘; dbms_system.set_ev(sid,serial#,10046,0,‘username‘); exec dbms_system.set_ev(135,694,10046,12,‘‘); ============= 10053 事件 了解oracle执行计划的生成过程 无法获知代价的计算公式 sys.dbms_system.set_ev(sid,serial#,10053,1|2|0) create table t as select * from t1 where rownum<100; create index idx_t on t(object_id); create index idx_t1 on t1(object_id); begin dbms_stats.gather_table_stats( user, ‘t‘, cascade=>true, estimate_percent=>null, method_opt=>‘for all columns size 1‘ ); end; / alter session set events ‘10053 trace name context forever,level 1‘; select count(*) from t,t1 where t.object_id=t1.object_id; 10053 trace的内容 参数区 sql区 系统信息区 system statistics information 基本统计信息 base statistical information 数据访问 access path 关联查询 join order 代价的最后修正 最终执行计划 当set autotrace 或者explain plan显示错误的执行计划,而找不到原因 ============ 初始化参数和性能视图 性能问题的来源和对应的性能视图 CPU cpu_count 逻辑cpu数量(thread) 对并行和代价有影响 内存 memory_target 自动管理内存特性 memory_max_target sga_target pga_aggregate_target pga内存空间总和动态调整 I/O db_file_multiblock_read_count 多块数据的读取 --多块读只在下面两种情形下 FTS index ffs db_writer_processes 设置多个db_writer进程,加快数据从缓冲区写入磁盘的速度 disk_asynch_io 网络 processes 允许产生的process数量 sessions 允许产生的session数量 process和session的关系 -process表示操作系统级别的一个进程 -session 表示process和数据库建立的会话数量 open_cursors 某个会花能够打开的最大游标数 默认值50 取值0-65535 优化器 optimizer_index_cost_adj CBO计算成本时索引的权重修正值 性能视图 CPU -v$sysstat -v$sesstat 某个会话消耗cpu的值 内存 -v$memory_target_advice oracle自动管理内存的建议器 -v$sga_target_advice -v$pga_target_advice -gv$shared_pool_advice I/O -v$iostat_file 显示各种文件的I/O统计信息 数据文件,临时文件,控制文件,日志文件,归档文件 对象 -v$segstat 获得某对象的各类统计信息 网络会话 -v$session 某会话的当前各种状态,比如关联v$sql视图查看当前会话的sql语句 -v$session_wait 会话当前等待事件的详细信息 -v$session_event 会话所有等待事件的详细信息 -sesstat 会话的资源统计信息 ================ AWR & ASH报告 automatic workload repository(AWR) AWR报告的信息来源 select table_name from dict where table_name like ‘DBA_HIST_%‘; 手工统计AWR数据 select snap_id,name,value from dba_hist_sca where snap_id>=1178 and snap_id<=1180; 生成AWR图形曲线 DBA_HIST --> ODBC -->EXCEL AWR in Enterprise Manager 企业管理器 AWR基线(baseline) dbms_workload_repository.create_baseline() AWR报告生成 $ORACLE_HOME/rdbms/admin awrrpt.sql @/u01/app/oracle/product/11.2.0/dbhome_1/rdbms/admin/awrrpt.sql --header --load profile 负载 --top 5等待事件 --cpu & memory --RAC信息 --time model statistics --foreground wait 前台 --background wait events 后台 --instance axtivity statistics 实例 --tablespace IO statistics --buffer pool advisory --pga advisory --latch statistics --segments access statistics AWR报告的阅读方法 1.了解数据库和业务情况 2.有侧重点的阅读 3.按照一条线索来阅读 4.面-线-点的方式 ================== ASH active session history ASH报表间隔时间可以精确到分钟,因而ASH可以提供比AWR更详细的关于历史会话的信息,可以作为AWR的补充 信息来源: -v$active_session_history 当前会话的采样数据,1秒钟一次快照 -dba_hist_active_sess_history (保留v$active_session_history更早的历史数据) ASH AWR SQL_TRACE 产生ASH报告 运行 ashrpt.sql 通过sql查询性能差的sql select sum(a.time_waited) total_time from v$active_session_history a, v$event_name b where a.event# = b.event# and sample_time > ‘21-nov-18 12:00:00 AM‘ and sample_time < ‘21-nov-18 05:00:00 AM‘ and b.wait_class = ‘USER I/O‘ ASH报告 -top user events -top events 等待事件的明细 -top sql -top sessions -top objects & files AWR & ASH 1.对业务了解 2.建立基线数据做对比 3.有目的性的阅读
以上是关于oracle性能优化的主要内容,如果未能解决你的问题,请参考以下文章
Oracle性能优化之性能调整_超越OCP精通Oracle视频教程培训38
Oracle性能优化之性能诊断工具_超越OCP精通Oracle视频教程培训33
Oracle性能优化之性能跟踪工具_超越OCP精通Oracle视频教程培训34