1. 查看具体语句的执行计划及消耗的内存/CPU量
当前两张表中各有数据1508875条
原始语句为:
SELECT/*+rule*/ JB.RYID, JB.GMSFHM,JB.XM, JB.XB, JB.CSRQ, ZP.ZP
FROMHZCZRK_JBXXB JB,HZCZRK_ZPXXB ZP
WHEREJB.RYID = ZP.RYID
AND ( JB.RYID>= 1 AND JB.RYID < 10001)
采用以下方式可以获得该语句的执行计划:
执行计划
----------------------------------------------------------
Plan hashvalue: 2567408823
--------------------------------------------
| Id | Operation | Name |
--------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | MERGE JOIN | |
| 2 | SORT JOIN | |
| 3 | TABLE ACCESS FULL| HZCZRK_ZPXXB |
|* 4 | SORT JOIN | |
|* 5 | TABLE ACCESS FULL| HZCZRK_JBXXB |
--------------------------------------------
PredicateInformation (identified by operation id):
---------------------------------------------------
4 -access("JB"."RYID"="ZP"."RYID")
filter("JB"."RYID"="ZP"."RYID")
5 -filter("JB"."RYID"<10001 AND"JB"."RYID">=1)
Note
-----
- rule based optimizer used (consider usingcbo)
统计信息
----------------------------------------------------------
2 recursive calls
1 db block gets
544 consistent gets
0 physical reads
176 redo size
11999 bytes sent via SQL*Net to client
811 bytes received via SQL*Net from client
7 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
79 rows processed
如果不设置总行数,则执行计划是:
执行计划
----------------------------------------------------------
Plan hashvalue: 2567408823
--------------------------------------------
| Id | Operation | Name |
--------------------------------------------
| 0 | SELECT STATEMENT | |
| 1 | MERGE JOIN | |
| 2 | SORT JOIN | |
| 3 | TABLE ACCESS FULL| HZCZRK_ZPXXB |
|* 4 | SORT JOIN | |
| 5 | TABLE ACCESS FULL| HZCZRK_JBXXB |
--------------------------------------------
PredicateInformation (identified by operation id):
---------------------------------------------------
4 - access("JB"."RYID"="ZP"."RYID")
filter("JB"."RYID"="ZP"."RYID")
Note
-----
- rule based optimizer used (consider usingcbo)
统计信息
----------------------------------------------------------
2 recursive calls
1 db block gets
1103 consistent gets
0 physical reads
176 redo size
23915 bytes sent via SQL*Net to client
866 bytes received via SQL*Net from client
12 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts(disk)
164 rows processed
这里我们主要需要关注两个点:consistent gets代表内存消耗,physical reads代表磁盘I/O消耗,单位都是数据块(DB_BLOCK_SIZE)。
从上面的执行计划可以看出表之间的连接关系基本上就是采用排序合并连接技术,所以下面对连接技术做出一些说明
适合于大批量数据处理的连接技术只有两种:
1. 排序合并连接(Sort/Merge)技术
两个表先按连接字段进行排序,再将两个表的排序结果进行顺序匹配,将合并结果返回给客户。
2. 哈希连接(HASH)技术
A hashjoin is executed as follows:
Bothtables are split into as many partitions as required, using a full table scan.
For eachpartition pair, a hash table is built in memory on the smallest partition.
The otherpartition is used to probe the hash table.
两种技术都适合于大表与大表的查询,而且通常情况下,HASH优于Merge,更优于嵌套循环(Nested_Loop)连接技术,尤其是当HASH与Oracle并行处理技术相结合的情况下,将极大地提高系统的整体吞吐量。
2. 对两张表做一个分析
由于从Oracle10G开始已经不采用基于规则优先的算法,而是交由RBO来进行,所以需要在优化前对表进行分析
execute dbms_stats.gather_table_stats(ownname =>‘SYSTEM‘,tabname => ‘HZCZRK_JBXXB‘ ,estimate_percent => null ,method_opt=> ‘for all indexed columns‘ ,cascade => true);
分析结果可以在这里看:
selecta.owner,a.segment_name,a.segment_type,a.tablespace_name,round(a.bytes/1024/1024/1024,2)tablesize,b.last_analyzed
from dba_segments a,dba_tables b
wherea.segment_name=b.table_name;
看起来表一切正常。
3. 看V$Session_longops,超过6秒的SQL
select * from V$Session_longopsorder BY last_update_time DESC;
只要是涉及两张表的全表查询都会超过6秒。
4. 去掉Hint基于规则优先/*rule/看几次语句执行后的执行计划及消耗的内存/CPU量
SELECT JB.RYID, JB.GMSFHM,JB.XM, JB.XB, JB.CSRQ, ZP.ZP
FROM HZCZRK_JBXXB JB,HZCZRK_ZPXXB ZP
WHERE JB.RYID = ZP.RYID;
执行计划是:
执行计划
----------------------------------------------------------
Plan hashvalue: 993606438
--------------------------------------------------------------------------------
---
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|
--------------------------------------------------------------------------------
---
| 0 | SELECT STATEMENT | | 1 | 49 | 48728 (1)| 00:09:4
5 |
|* 1 | HASH JOIN | | 1 | 49 | 48728 (1)| 00:09:4
5 |
| 2 | TABLE ACCESS FULL| HZCZRK_JBXXB | 1 | 41 | 2 (0)| 00:00:0
1 |
| 3 | TABLE ACCESS FULL| HZCZRK_ZPXXB | 61692 | 481K| 48725 (1)| 00:09:4
5 |
--------------------------------------------------------------------------------
---
PredicateInformation (identified by operation id):
---------------------------------------------------
1 -access("JB"."RYID"="ZP"."RYID")
统计信息
----------------------------------------------------------
8 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
830 bytes sent via SQL*Net to client
756 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
连接关系由Merge join和Sort join转变为Hash Join。
5. 尝试在RYID、身份证号码、姓名这些字段建复合索引,然后看执行计划及消耗的内存/CPU量
CREATE INDEX inx_ryid_HZCZRK_JBXXB onHZCZRK_JBXXB(RYID,XM,CSRQ);
CREATE INDEX inx_ryid_HZCZRK_ZPXXB onHZCZRK_ZPXXB(RYID,ZPID);
之后执行计划为:
执行计划
----------------------------------------------------------
Plan hashvalue: 993606438
--------------------------------------------------------------------------------
---
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|
--------------------------------------------------------------------------------
---
| 0 | SELECT STATEMENT | | 1 | 49 | 48728 (1)| 00:09:4
5 |
|* 1 | HASH JOIN | | 1 | 49 | 48728 (1)| 00:09:4
5 |
| 2 | TABLE ACCESS FULL| HZCZRK_JBXXB | 1 | 41 | 2 (0)| 00:00:0
1 |
| 3 | TABLE ACCESS FULL| HZCZRK_ZPXXB | 61692 | 481K| 48725 (1)| 00:09:4
5 |
--------------------------------------------------------------------------------
---
PredicateInformation (identified by operation id):
---------------------------------------------------
1 -access("JB"."RYID"="ZP"."RYID")
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
830 bytes sent via SQL*Net to client
756 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
可以看到内存消耗量更加小了。
6. 尝试复合的Global Partition索引,然后看执行计划及消耗的内存/CPU量
以出生年月来进行范围的划分,在表HZCZRK_JBXXB上创建索引CSRQ_IX_RANGE
CREATE INDEXCSRQ_IX_RANGE ON HZCZRK_JBXXB(CSRQ)
GLOBALPARTITION BY RANGE(CSRQ)
(
PARTITIONP_19021912 VALUES LESS THAN (19130101),
PARTITIONP_19131923 VALUES LESS THAN (19240101),
PARTITIONP_19241934 VALUES LESS THAN (19350101),
PARTITIONP_19351945 VALUES LESS THAN (19460101),
PARTITIONP_19561966 VALUES LESS THAN (19670101),
PARTITIONP_19671977 VALUES LESS THAN (19780101),
PARTITIONP_19781988 VALUES LESS THAN (19890101),
PARTITIONP_19891999 VALUES LESS THAN (20000101),
PARTITIONP_GREATER_THAN_20120101 VALUES LESS THAN (MAXVALUE)
);
创建索引后采用MapReduce方式对出生日期按照年的方式导出数据,读的速度加快很多。
7. 确保两张表的所有字段,特别是索引字段为同一数据类型
这个经过检查可以确认对应字段全部一致。
8. 尝试晴空内存空间(Shared Pool),然后看执行计划及消耗的内存/CPU量
alter system flush shared_pool;
alter system flush buffer_cache;
9. 避免重复语句解析(Parse)
这里Java代码中执行语句中RYID是采用参数传入的方式,所以避免了重复语句解析的状况出现。
10. 增加PGA参数看看效果
监控PGA的视图:v$sql_workarea_active、v$sql_workarea、v$sesstat、v$process、v$sysstat、v$sql_workarea_histogram等。
使用下面的视图查看Oracle建议的评估设置:
Select pga_target_for_estimate/1024/1024 ||‘M‘ "Estimate PGA Target"
,estd_pga_cache_hit_percentage "CacheHit(%)"
,estd_extra_bytes_rw/1024/1024 ||‘M‘"Extra Read/Write"
,estd_overalloc_count "Over alloccount"
From v$pga_target_advice
选出的4个列中,Over alloc count指示Oracle SQL工作区内存分配的三种情况:optimal完全可以在内存中完成操作;onepass需要进行一次磁盘交换;multipass需要进行多次磁盘交换。第四列的值就是需要进行磁盘交换的数量。
PGA_AGGREGATE_TARGET的值最好选择: Over alloc count为0、Cache Hit(%)尽可能高、Extra Read/Write尽可能低的Estimate PGA Target值。
l PGA_AGGREGATE_TARGET参数用以在PGA自动管理模式下,设置所有用户会话对PGA内存的使用限制,也就是说,PGA在自动管理模式下,能够妥善地利用PGA的内存空间。而对于需要大量内存空间来进行运算的SQL语句来说,Oracle能够优先分配足够的空间,并尽量将其作业保持在Optimal Size类型的工作区域中以增加效率,PGA_AGGREGATE_TARGET的大小范围是10 MB至4 GB。
联机事务处理系统(On-LineTransaction Processing System,OLTP)公式为:PGA_ AGGREGATE_TARGET = (物理内存大小* 80%) * 20%。
而决策支持系统(DecisionSupport Systems,DSS)的公式为:PGA_AGGREGATE_ TARGET = (物理内存大小* 80%) * 50%。
ALTER SYSTEM SET pga_aggregate_target=1024M;
l 设置数据高速缓冲区大小
ALTER SYSTEM SET db_cache_size=1024;
l 设置共享池大小
ALTER SYSTEM SET shared_pool_size=1024;
l 设置Java池大小
ALTER SYSTEM SET java_pool_size=1024;
l 设置大型池大小
ALTER SYSTEM SET large_pool_size=1024;
l 设置SGA内存总和
ALTER SYSTEM SET sga_target=1024;
11. 查看Redo_Log文件块
重做日志文件块过多也会造成系统性能下降,可以通过以下语句查找重做日志文件,
select * fromv$logfile order by GROUP#;
如果需要清除重做日志文件组,可以使用以下语句:
alter databaseclear logfile group 2;该语句会清除文件组2的日志文件内容
12. 分区表技术介绍
请根据项目的实际情况选择采用哪种分区技术实践
l 范围(Range)分区,按一张表指定的一个字段值或多个字段值的范围进行分区,一般适用于按时间周期进行数据的存储,缺点是字段记录值变动太多的话将导致记录在分区之间移动太频繁,影响性能。
l 哈希(Hash)分区,是指Oracle通过一个内部的Hash散列算法,以分区字段值为输入,进行散列运算,返回一个分区值,最后自动将记录插入到该分区。最大的特点是记录被Oracle均匀分布到各分区。适合于静态数据。
l 列表(list)分区,通过对分区字段的散列值进行分区,或者说以枚举方式进行分区。该分区是不排序的,而且分区之间没有关联关系,另外,只支持单个字段。与范围分区的差别在列表分区是按记录的离散值进行分区的,适用场景和优缺点完全一致。
l 间隔(Interval)分区,通过该分区技术,用户可指定时间间隔,例如假设按月进行分区,则Oracle在每个新月到来时,自动创建该月的分区,免去了DBA此方面的管理工作。
l 基于虚拟列(VirtualColumn-Based)的分区技术,支持在字段函数基础上进行分区,例如to_char,substr这些函数。
l 引用(Reference)分区技术,支持通过外键直接管理子表的分区。
l 系统(system)分区,允许应用程序控制写入记录到哪个分区。
13. 对索引进行碎片分析,如果需要则整理
l 查表的索引及状态
select *from USER_INDEXES where table_name = ‘HZCZRK_JBXXB‘
l 2. 开启Oracle索引监控
ALTERINDEX SYS_C0010867 MONITORING USAGE
l 3. 执行sql语句
l 4. 关闭Oracle索引监控
ALTERINDEX SYS_C0010867 NOMONITORING USAGE
l 5. 查看索引是否有被使用
SELECT *FROM V$OBJECT_USAGE;
l 6. 索引碎片分析
ANALYZEINDEX SYS_C0010867 VALIDATE STRUCTURE ONLINE;
SELECTNAME,DEL_LF_ROWS_LEN,LF_ROWS_LEN,(DEL_LF_ROWS_LEN/LF_ROWS_LEN)*100 FROMINDEX_STATS;
如果碎片率超过20%,则需要整理
l 7. 碎片整理
ALTERINDEX SYS_C0010867 REBUILD;
l 8.碎片压缩
ALTER INDEX SYS_C0010867 COALESCE;
14. 在线重新定义表技术
DBMS_REDEFINITION支持在几乎不中断业务的情况下,通过创建一个中间表,并通过内部机制,保证原表与中间表的数据同步,最后通过一个切换操作,完成表结构的在线重新定义,即非分区表向分区表的转换,或者已分区表向另一种分区表的转换等。
15. 尝试并行执行语句/*+Parallel*/
l SELECT /*+PARALLER()*/ JB.RYID, JB.GMSFHM,JB.XM, JB.XB, JB.CSRQ,ZP.ZP
FROMHZCZRK_JBXXB JB,HZCZRK_ZPXXB ZP
WHEREJB.RYID = ZP.RYI;
Oracle在执行该语句时,将根据该表的并行度(DOP)定义或者根据语句的并行处理HINT自动从并行处理缓冲池中分配相应数量的并行过程进行并行操作,并行处理协调器(Parallel Execution Coordinator)自动将各并行子进程处理的结果合并返回给客户,并释放并行子进程返回到缓冲池中。
执行计划如下:
----------------------------------------------------------
Plan hashvalue: 993606438
--------------------------------------------------------------------------------
---
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|
--------------------------------------------------------------------------------
---
| 0 | SELECT STATEMENT | | 4714 | 478K| 3050 (1)| 00:00:3
7 |
|* 1 | HASH JOIN | | 4714 | 478K| 3050 (1)| 00:00:3
7 |
| 2 | TABLE ACCESS FULL| HZCZRK_JBXXB | 4713 | 405K| 30 (0)| 00:00:0
1 |
| 3 | TABLE ACCESS FULL| HZCZRK_ZPXXB | 17999 | 281K| 3019 (1)| 00:00:3
7 |
--------------------------------------------------------------------------------
---
PredicateInformation (identified by operation id):
---------------------------------------------------
1 -access("JB"."RYID"="ZP"."RYID")
Note
-----
- dynamic sampling used for this statement(level=2)
统计信息
----------------------------------------------------------
58 recursive calls
1 db block gets
25467 consistent gets
0 physical reads
240 redo size
557645 bytes sent via SQL*Net to client
3627 bytes received via SQL*Net from client
263 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
3924 rows processed
l 在对分区表进行处理时,Oracle会自动为每个分区分配一个进程,从而达到并行处理的目的。不仅分区之间可以并行处理,而且Oracle会自动根据资源使用情况和数据分布情况,自动在分区内部进行并行处理,因此可进一步提高大批量数据处理的吞吐量。
SELECT/*+PARALLER_INDEX(oi,ghoi_ix,8)*/ JB.RYID, JB.GMSFHM,JB.XM, JB.XB, JB.CSRQ,ZP.ZP
FROMHZCZRK_JBXXB JB,HZCZRK_ZPXXB ZP
WHERE JB.RYID = ZP.RYID;
语句的执行计划如下:
----------------------------------------------------------
Plan hashvalue: 993606438
--------------------------------------------------------------------------------
-----------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)|
Time |
--------------------------------------------------------------------------------
-----------
| 0 | SELECT STATEMENT | | 458K| 45M| | 38437 (1)|
00:07:42 |
|* 1 | HASH JOIN | | 458K| 45M| 43M| 38437 (1)|
00:07:42 |
| 2 | TABLE ACCESS FULL| HZCZRK_JBXXB | 458K| 38M| | 323 (5)|
00:00:04 |
| 3 | TABLE ACCESS FULL| HZCZRK_ZPXXB | 2439K| 37M| | 32674 (1)|
00:06:33 |
--------------------------------------------------------------------------------
-----------
PredicateInformation (identified by operation id):
---------------------------------------------------
1 - access("JB"."RYID"="ZP"."RYID")
Note
-----
- dynamic sampling used for this statement(level=2)
统计信息
----------------------------------------------------------
52 recursive calls
3 db block gets
265779 consistent gets
224230 physical reads
528 redo size
5738732 bytes sent via SQL*Net to client
30236 bytes received via SQL*Net from client
2682 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
40200 rows processed
16. 表空间迁移技术(Transportable Tablespace)
Transportable Tablespace技术是所有Oracle ETL工具中速度最快的!
17. 尝试物化表视图技术与语句重写技术
物化视图是一个实体,保存了从视图中产生的数据,尤其是汇总数据,需要消耗一定的硬盘资源。物化视图将进行统计运算、多表连接和其他复杂计算的SQL语句的结果,直接生成到定义的物化视图中。通过物化视图的各种数据刷新机制(COMPLETE、FAST、FORCE等),以及手工或自动等方式,来保持基表与物化视图数据的一致性。语句重写技术能自动将原有SQL统计运算导向到相应的物化视图,一方面大大提高了统计运算速度,另一方面又保证了对应用的透明性。在物化视图上可以创建相应的索引。
以下语句建立物化视图:
CREATEMATERIALIZED VIEW mview_ry_summary
TABLESPACEHZCZRK_DATA
PARALLEL(DEGREE1)
BUILD IMMEDIATE
REFRESH COMPLETEON DEMAND
ENABLE QUERYREWRITE
AS
SELECT JB.RYID,JB.GMSFHM,JB.XM, JB.XB, JB.CSRQ,"TO_LOB"(ZP.ZP)
FROMHZCZRK_JBXXB JB,HZCZRK_ZPXXB ZP
WHERE JB.RYID =ZP.RYID
18. 尝试不合并视图/*+no_merge*/,然后看执行计划及消耗的内存/CPU量
如果在查询中用到多个视图,而组成这些视图的SQL语句都是优化好了的,单独访问任何一个视图,性能都没有问题。如果此时不加NO_MERGE,则ORACLE会自动将若干个视图拆散,重新构造执行计划。而事实证时,重新构造的执行计划往往会比较糟糕,于是,这种情况下就可以利用NO_MERGE(按字面理解就是不把若干个视图的查询条件进行合并),避免ORACLE将视图的查询拆散。
加入后执行计划为:
----------------------------------------------------------
Plan hash value: 993606438
--------------------------------------------------------------------------------
---
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
|
--------------------------------------------------------------------------------
---
| 0 | SELECT STATEMENT | | 1 | 49 | 48728 (1)| 00:09:4
5 |
|* 1 | HASH JOIN | | 1 | 49 | 48728 (1)| 00:09:4
5 |
| 2 | TABLE ACCESS FULL|HZCZRK_JBXXB | 1 | 41 | 2 (0)| 00:00:0
1 |
| 3 | TABLE ACCESS FULL|HZCZRK_ZPXXB | 61692 | 481K| 48725 (1)| 00:09:4
5 |
--------------------------------------------------------------------------------
---
Predicate Information (identified byoperation id):
---------------------------------------------------
1 -access("JB"."RYID"="ZP"."RYID")
统计信息
----------------------------------------------------------
8 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
830 bytes sent via SQL*Net to client
756 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
0 rows processed
工作当中总结出来通过上述18项的若干项优化后确实可以提高Oracle的性能,但是设置各参数的值需要根据服务器实际情况来做调整。