1、oracle数据库访问的方法
在执行计划中,我经常看到有很多访问数据的方法如:
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%C
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 18M| 1589M| | 189K
|* 1 | HASH JOIN | | 18M| 1589M| 11M| 189K
| 2 | INDEX FAST FULL SCAN| UNI_MY_OBJ_ID | 661K| 3874K| | 568
| 3 | TABLE ACCESS FULL | MYTEST | 18M| 1483M| | 64386
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CP
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 110 | 9900 | 121 (
| 1 | NESTED LOOPS | | 110 | 9900 | 121 (
|* 2 | INDEX UNIQUE SCAN | UNI_MY_OBJ_ID | 1 | 6 | 2 (
| 3 | TABLE ACCESS BY INDEX ROWID| MYTEST | 110 | 9240 | 119 (
|* 4 | INDEX RANGE SCAN | MY_OBJ_ID | 116 | | 2 (
--------------------------------------------------------------------------------
TABLE ACCESS FULL,全表扫描,对表进行全表访问
INDEX FAST FULL SCAN,全索引扫描,对某个索引进行全部扫描
INDEX UNIQUE SCAN,唯一索引扫描,对某个主键或者唯一索引指定的值进行检索
INDEX RANGE SCAN,按一定范围对索引进行检索
下面我就来说说访问数据的方式:
--全表扫描(Full Table Scans, FTS)
是对表的所有块进行全部扫描,通过ROWID的表存取(Table Access by ROWID或rowid lookup)获取表的全部数据
--索引扫描(Index Scan或index lookup),我们列出几种情况
a、索引范围扫描(index range scan):范围扫描,如where后面的between,连接中的非唯一等值条件时用得较多
b、索引全扫描(index full scan):需要检索的值包含在索引中
c、索引快速扫描(index fast full scan):在连接条件中,只需要某表的索引做判断时,使用的是快速索引扫描
d、索引唯一扫描(index unique scan):主键或者唯一索引等值
e、索引跳跃扫描(INDEX SKIP SCAN):通常使用在组合索引中,对索引字段中,排在第二个及以后的值进行判断
2.oracle数据库连接的方式
--合并连接(Sort Merge Join (SMJ) )
a) 对于非等值连接,这种连接方式的效率是比较高的。
b) 如果在关联的列上都有索引,效果更好。
c) 对于将2个较大的row source做连接,该连接方法比NL连接要好一些。
d) 但是如果sort merge返回的row source过大,则又会导致使用过多的rowid在表中查询数据时,数据库性能下降,因为过多的I/O。
--嵌套循环(Nested Loops, NL):
a) 如果driving row source(外部表)比较小,并且在inner row source(内部表)上有唯一索引,或有高选择性非唯一索引时,使用这种方法可以得到较好的效率。
b) NESTED LOOPS有其它连接方法没有的的一个优点是:可以先返回已经连接的行,而不必等待所有的连接操作处理完才返回数据,这可以实现快速的响应时间。
--哈希连接(Hash Join, HJ):
a) 这种方法是在oracle7后来引入的,使用了比较先进的连接理论,一般来说,其效率应该好于其它2种连接,但是这种连接只能用在CBO优化器中,而且需要设置合适的hash_area_size参数,才能取得较好的性能。
b) 在2个较大的row source之间连接时会取得相对较好的效率,在一个row source较小时则能取得更好的效率。
c) 只能用于等值连接中
3.oracle成本计算
依据cpu消耗使用的成本计算
--According to the CPU costing model:
Cost = (#SRds * sreadtim + #MRds * mreadtim + #CPUCycles / cpuspeed) / sreadtim
where
#SRDs:number of single block reads
#MRDs:number of multi block reads
#CPUCycles:number of CPU Cycles
sreadtim:single block read time
mreadtim:multi block read time
cpuspeed:CPU cycles per second
--创建测试表
CREATE TABLE "HDNC57"."MYTEST"
( "OWNER" VARCHAR2(30),
"OBJECT_NAME" VARCHAR2(128),
"OBJECT_ID" NUMBER,
"OBJECT_TYPE" VARCHAR2(19),
"CREATED" DATE,
"LAST_DDL_TIME" DATE,
"STATUS" VARCHAR2(7),
"TIMESTAMP" VARCHAR2(19)
)
插入了大概1000多万的数据.
--进行统计分析
SQL> EXEC dbms_stats.gather_table_stats('TEST_USER','MYTEST',cascade =>true,estimate_percent => 50,method_opt => 'for all columns size 1' );
PL/SQL procedure successfully completed.
--查看实际的IO和CPU成本
SQL> alter system flush shared_pool;
System altered.
SQL> alter session set events '10053 trace name context forever, level 2';
Session altered.
SQL> select count(*) from test_user.mytest where object_id >= 400000;
COUNT(*)
----------
154560
提取跟踪文件相应数值:
跟踪文件:
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for MYTEST[MYTEST]
Column (#3): OBJECT_ID(
AvgLen: 5 NDV: 165297 Nulls: 0 Density: 0.000006 Min: 2 Max: 417809
Table: MYTEST Alias: MYTEST
Card: Original: 18511062.000000 Rounded: 789145 Computed: 789144.97 Non Adjusted: 789144.97
Access Path: TableScan
Cost: 64361.19 Resp: 64361.19 Degree: 0
Cost_io: 64195.00 Cost_cpu: 6130585710
Resp_io: 64195.00 Resp_cpu: 6130585710
Access Path: index (index (FFS))
Index: MY_OBJ_ID
resc_io: 11741.00 resc_cpu: 3501331838
ix_sel: 0.000000 ix_sel_with_filters: 1.000000
Access Path: index (FFS)
Cost: 11835.92 Resp: 11835.92 Degree: 1
Cost_io: 11741.00 Cost_cpu: 3501331838
Resp_io: 11741.00 Resp_cpu: 3501331838
Access Path: index (IndexOnly)
Index: MY_OBJ_ID
resc_io: 1850.00 resc_cpu: 173299064
ix_sel: 0.042631 ix_sel_with_filters: 0.042631
Cost: 1854.70 Resp: 1854.70 Degree: 1
Best:: AccessPath: IndexRange
Index: MY_OBJ_ID
Cost: 1854.70 Degree: 1 Resp: 1854.70 Card: 789144.97 Bytes: 0
oracle基于cbo分析比较后,认为采用index (IndexOnly)最优,所以得出了索引范围扫描的执行计划成本,根据索引范围扫描的成本为1855,具体的执行计划如下
SQL> explain plan for SELECT COUNT(*) "COUNT(*)" FROM "TEST_USER"."MYTEST" "MYTEST" WHERE "MYTEST"."OBJECT_ID">=400000;
Explained.
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2285605767
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 5 | 1855 (1)| 00:00:23 |
| 1 | SORT AGGREGATE | | 1 | 5 | | |
|* 2 | INDEX RANGE SCAN| MY_OBJ_ID | 789K| 3853K| 1855 (1)| 00:00:23 |
-------------------------------------------------------------------------------
所以根据cbo,采用了范围索引扫描。
4、手动计算成本
SQL> select pname, pval1 from sys.aux_stats$ where sname='SYSSTATS_MAIN';
PNAME PVAL1
------------------------------ ----------
CPUSPEEDNW 3074.07407
IOSEEKTIM 10
IOTFRSPEED 4096
SREADTIM
MREADTIM
CPUSPEED
MBRC
MAXTHR
SLAVETHR
SQL> show parameter DB_FILE_MULTIBLOCK_READ_COUNT;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
db_file_multiblock_read_count integer 30
该数值匹配到计算公式中mbrc
--根据执行计划来看,采用的是索引范围扫描,手动根据公司计算如下:
COST = IO COST + CPUCOST
IO COST = blevel+(leaf_blocks*ix_sel)+(clustering_factor*ix_sel_with_filters)
CPU COST = #cpucycles/(cpuspeed或者cpuspeednw) / 1000 / sreadtim
sreadtim = ioseektim + db_block_size / iotfrspeed = 10 + 8192 / 4096 = 12
IO COST计算需要统计信息
统计信息如下:
TABLE_NAME INDEX_NAME BLEVEL LEAF_BLOCKS DISTINCT_KEYS CLUSTERING_FACTOR NUM_ROWS
-------------- ---------------- ---------- ----------- ------------- ----------------- ----------
MYTEST MY_OBJ_ID 2 43346 165297 18780224 18780270
IO COST = 2 + 43346 * 0.042631 = 1850
cpu成本如下:
CPU COST = 173299064 / 3074.07407 / 1000 / 12 = 4.69
所以总体成本为:
1849.883 + 4.69 = 1850 + 5 = 1855
说明的几点:
--IO COST = blevel+(leaf_blocks*ix_sel)+(clustering_factor*ix_sel_with_filters) 的计算过程中,(clustering_factor*ix_sel_with_filters)值为0,因为这一段表示的主要是对索引进行范围扫描,无再过滤可能
--resc_cpu: 173299064 值为毫秒,10053里面评估出来的值
--ix_sel:由于where条件中只有大于> ,所以他是开放,根据索引的统计:
AvgLen: 5 NDV: 165297 Nulls: 0 Density: 0.000006 Min: 2 Max: 417809,绑定的值是400000
(max - 当前值) / (max - min) = (417809 - 400000) / 417807 = 17809 / 417807 = 0.042629,与10053略有偏差。
5、其它扫描方式的成本计算
--全表扫描:
COST = IO COST + CPUCOST
IO COST = ceil(blocks/mbrc)*mreadtim/sreadtim
CPU COST =#cpucycles/(cpuspeed或者cpuspeednw)/ 1000/sreadtim
IO COST 和CPU COST前半部分主要计算所花费的时间,最后除以单块访问的时间用于换算成单块读需要读取的块数。
--索引唯一扫描:
COST = IO COST + CPUCOST
IO COST = blevel +1 + 1
CPU COST =#cpucycles/(cpuspeed或者cpuspeednw)/ 1000/sreadtim
IO COST 第一步+1是访问的叶子块,最后一步+1是统计回表的块,因为唯一索引回表块数也是1。
--索引全扫描:
COST = IO COST + CPUCOST
IO COST = blevel+leaf_blocks+(clustering_factor*ix_sel_wiht_filters)
CPU COST =#cpucycles/(cpuspeed或者cpuspeednw)/ 1000/sreadtim
索引全扫描是单块顺序读,扫描的结果有序;
(clustering_factor*ix_sel_wiht_filter)表示回表的IO COST,如果不回表则不需要计算这一步,索引全扫描可以回表,但是索引快速全扫描不会回表;
ix_sel:因为索引全扫描是扫描所有的叶子块,不需要涉及到索引块的选择率,所以ix_sel是1;
ix_sel_with_filters:既然走索引全扫描,则该索引就不会存在过滤的可能,因为如果索引有过滤的可能,则直接走该单索引的范围扫描,无再过滤值,所以ix_sel_with_filters也是1。
--索引快速全扫描:
COST = IO COST + CPUCOST
IO COST = ceil(leaf_blocks/ mbrc)*mreadtim/sreadtim
CPU COST =#cpucycles/(cpuspeed或者cpuspeednw)/ 1000/sreadtim
索引快速全扫描是多块读,扫描结果无序,不需要回表;
ix_sel、ix_sel_with_filters 这2个参数在索引快速全扫描中无效,因为索引快速全扫描是多块读且不需要回表。
--索引跳跃扫描:
COST = IO COST + CPUCOST
若叶子块数少于前导列的不同值数,IO COST以叶子块数来计算。
IO COST = blevel+ leaf_blocks+(CLUSTERING_FACTOR*ix_sel_filter)
若叶子块数多于前导列的不同值数,IO COST以前导列不同值数来计算。
IO COST = blevel+前导列不同值数*ceil(leaf_blocks/distinct_keys) +( CLUSTERING_FACTOR*ix_sel_with_filters)
CPU COST =#cpucycles/(cpuspeed或者cpuspeednw)/ 1000/sreadtim
ix_sel=ix_sel_with_filters:是where条件中走该索引谓词(一个/多个相乘)的选择率
跳跃扫描不仅仅出现在无第一个前导列时,如果复合索引包括三个字段,where条件中没有第二个字段的查询列,但有第一和第三的查询列时,
其实是可以走索引跳跃扫描,相当于跳过第二个字段,ix_sel和ix_sel_with_filters就是第一列和第三列的选择率相乘。而且也等于10053事件trace的ss sel这个参数;
前导列不同值数非常大时走索引跳跃扫描的成本会很高。