收集统计信息
问题测试:
获取自动收集统计信息JOB的信息
1* select log_id,log_date,owner,job_name,status,req_start_date,actual_start_date,run_duration,cpu_used from dba_scheduler_job_run_details where lower(job_name) like '%gather_stats_job%' order by log_id
LOG_ID LOG_DATE OWNER JOB_NAME STATUS ACTUAL_START_DATE RUN_DURATION CPU_USED
------ ----------------------------------- ----- ------------------------- ---------- ------------------------------ ------------------------- -------------------------
436 2014-07-31 22:55:17.477559 SYS GATHER_STATS_JOB SUCCEEDED 2014-07-31 22:54:53.775509 +000 00:00:24 +000 00:00:05.31
451 2014-08-02 09:33:01.565868 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-02 09:32:15.493005 +000 00:00:46 +000 00:00:11.20
493 2014-08-05 02:46:53.737519 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-05 02:46:18.885318 +000 00:00:35 +000 00:00:07.18
515 2014-08-07 04:28:10.499073 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-07 04:27:39.841948 +000 00:00:31 +000 00:00:08.03
534 2014-08-09 10:30:31.265575 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-09 10:29:27.029581 +000 00:01:04 +000 00:00:07.35
570 2014-08-11 22:20:18.896573 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-11 22:19:29.831058 +000 00:00:49 +000 00:00:08.36
602 2014-08-12 23:18:26.573014 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-12 23:17:49.866337 +000 00:00:37 +000 00:00:08.41
633 2014-08-13 22:17:33.236311 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-13 22:16:49.790815 +000 00:00:43 +000 00:00:09.04
657 2014-08-15 02:32:11.726540 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-15 02:31:35.913797 +000 00:00:36 +000 00:00:10.00
685 2014-08-19 02:49:17.709531 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-19 02:46:58.042017 +000 00:02:20 +000 00:00:37.87
715 2014-08-21 01:48:24.366631 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-21 01:45:49.330810 +000 00:02:35 +000 00:00:19.26
LOG_ID LOG_DATE OWNER JOB_NAME STATUS ACTUAL_START_DATE RUN_DURATION CPU_USED
------ ----------------------------------- ----- ------------------------- ---------- ------------------------------ ------------------------- -------------------------
725 2014-08-24 23:27:47.297346 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-24 23:27:21.684993 +000 00:00:26 +000 00:00:05.28
12 rows selected.
dbms_scheduler.run_job官方注解
RUN_JOB Procedure
This procedure runs a job immediately.
Syntax
DBMS_SCHEDULER.RUN_JOB (
job_name IN VARCHAR2,
use_current_session IN BOOLEAN DEFAULT TRUE);
Parameters
Table 93-48 RUN_JOB Procedure Parameters
|
Parameter
|
Description
|
|
job_name
|
The name of the job being run
|
|
use_current_session
|
This specifies whether the job run should occur in the same session as the one that the procedure was invoked from.
When use_current_session is set to TRUE:
-
You can test a job and see any possible errors on the command line.
-
run_count, last_start_date, last_run_duration, and failure_count are not updated.
-
RUN_JOB can be run in parallel with a regularly scheduled job run.
When use_current_session is set to FALSE:
-
You need to check the job log to find error information.
-
run_count, last_start_date, last_run_duration, and failure_count are updated.
-
RUN_JOB fails if a regularly scheduled job is running.
|
Usage Notes
The job does not have to be enabled. If the job is disabled, the following validity checks are performed before running it:
-
The job points to a valid job class.The job owner has EXECUTE privileges on the job class.If a program or chain is referenced, the program/chain exists.If a program or chain is referenced, the job owner has privileges to execute the program/chain.All argument values have been set (or have defaults).The job owner has the CREATE EXTERNAL JOB privilege if this is an external job.
The job can be run in two different modes. One is in the current user session. In this case, the call to RUN_JOB will block until it has completed the job. Any errors that occur during the execution of the job will be returned as errors to the RUN_JOB procedure. The other option is to run the job immediately like a regular job. In this case, RUN_JOB returns immediately and the job will be picked up by the coordinator and passed on to a job slave for execution. The Scheduler views and logs must be queried for the outcome of the job.
Multiple user sessions can use RUN_JOB in their sessions simultaneously when use_current_session is set to TRUE.
When using RUN_JOB with jobs that point to chains, use_current_session must be FALSE.
RUN_JOB requires that you be the owner of the job or have ALTER privileges on that job. You can also run a job if you have the CREATE ANY JOB privilege.
|
手工调用自动收集统计信息job
SQL> exec dbms_scheduler.run_job('GATHER_STATS_JOB',true);
PL/SQL procedure successfully completed.
再次查看自动收集统计信息信息
SQL> col job_name for a25
SQL> alter session set nls_timestamp_tz_format='yyyy-mm-dd hh24:mi:ss.ff';
Session altered.
SQL> select log_id,log_date,owner,job_name,status,req_start_date,actual_start_date,run_duration,cpu_used from dba_scheduler_job_run_details where lower(job_name) like '%gather_stats_job%' order by log_id;
LOG_ID LOG_DATE OWNER JOB_NAME STATUS ACTUAL_START_DATE RUN_DURATION CPU_USED
------ ----------------------------------- ----- ------------------------- ---------- ------------------------------ ------------------------- -------------------------
436 2014-07-31 22:55:17.477559 SYS GATHER_STATS_JOB SUCCEEDED 2014-07-31 22:54:53.775509 +000 00:00:24 +000 00:00:05.31
451 2014-08-02 09:33:01.565868 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-02 09:32:15.493005 +000 00:00:46 +000 00:00:11.20
493 2014-08-05 02:46:53.737519 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-05 02:46:18.885318 +000 00:00:35 +000 00:00:07.18
515 2014-08-07 04:28:10.499073 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-07 04:27:39.841948 +000 00:00:31 +000 00:00:08.03
534 2014-08-09 10:30:31.265575 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-09 10:29:27.029581 +000 00:01:04 +000 00:00:07.35
570 2014-08-11 22:20:18.896573 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-11 22:19:29.831058 +000 00:00:49 +000 00:00:08.36
602 2014-08-12 23:18:26.573014 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-12 23:17:49.866337 +000 00:00:37 +000 00:00:08.41
633 2014-08-13 22:17:33.236311 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-13 22:16:49.790815 +000 00:00:43 +000 00:00:09.04
657 2014-08-15 02:32:11.726540 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-15 02:31:35.913797 +000 00:00:36 +000 00:00:10.00
685 2014-08-19 02:49:17.709531 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-19 02:46:58.042017 +000 00:02:20 +000 00:00:37.87
715 2014-08-21 01:48:24.366631 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-21 01:45:49.330810 +000 00:02:35 +000 00:00:19.26
LOG_ID LOG_DATE OWNER JOB_NAME STATUS ACTUAL_START_DATE RUN_DURATION CPU_USED
------ ----------------------------------- ----- ------------------------- ---------- ------------------------------ ------------------------- -------------------------
725 2014-08-24 23:27:47.297346 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-24 23:27:21.684993 +000 00:00:26 +000 00:00:05.28
751 2014-08-27 04:58:12.980583 SYS GATHER_STATS_JOB SUCCEEDED 2014-08-27 04:57:51.591701 +000 00:00:21 +000 00:00:08.08
13 rows selected.
创建测试表
[ora10g@seconary ~]$ sqlplus tbs_11204/system
SQL*Plus: Release 10.2.0.5.0 - Production on Wed Aug 27 05:15:55 2014
Copyright (c) 1982, 2010, Oracle. All Rights Reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.5.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
SQL> create table t_stat(a int,b int);
Table created.
插入数据
SQL> insert into t_stat select level,level from dual connect by level<=1000;
1000 rows created.
SQL> commit;
Commit complete.
确认没有收集统计信息
SQL> set linesize 300
SQL> select table_name,num_rows,blocks,degree,avg_row_len,chain_cnt from user_tables where lower(table_name)='t_stat';
TABLE_NAME NUM_ROWS BLOCKS DEGREE AVG_ROW_LEN CHAIN_CNT
------------------------------ ---------- ---------- ---------- ----------- ----------
T_STAT 1
SQL>
手工执行上述自动收集统计信息的job
SQL> exec dbms_scheduler.run_job('GATHER_STATS_JOB',true);
PL/SQL procedure successfully completed.
再次查看上述表已收集了统计信息
SQL> select table_name,num_rows,blocks,degree,avg_row_len,chain_cnt from user_tables where lower(table_name)='t_stat';
TABLE_NAME NUM_ROWS BLOCKS DEGREE AVG_ROW_LEN CHAIN_CNT
------------------------------ ---------- ---------- ---------- ----------- ----------
T_STAT 1000 5 1 7 0
再回到开始的问题,从上述测试来看,可能原因就是tmp前缀的表刚好在自动统计信息收集完时间窗口外进行DML操作,所以没有统计信息
进一步分析,如果作过数据库开发的人,可能会猜测这种TMP前缀的表多用于存储过程之中,用于临时存储存储过程中间加工的结果,
可以通过如下脚本获取tmp前缀的表是否位于存储过程中
select owner,name from dba_source where lower(text) like '%tmp前缀的表%';
如果位于存储过程,存储过程是否定时运行呢,如果是这样的话,有种可能就是每次执行时tmp前缀的表在存储过程执行完,就drop table了
所以即使第2次自动收集统计信息运行,当然不会收集不存在的对象了
tmp前缀的表是否频繁创建删除,可通过如下脚本定位与诊断,主要看created,last_ddl_time列的时间,是不是很新的时间,
大家知道自动统计信息每天都会执行,所以可以在下述脚本在2天重复运行,对比下,就知道tmp前缀的表是否频繁drop然后重建了
select owner,object_name,object_id,object_type,created,last_ddl_time,timestamp,status
from dba_objects
where object_name='tmp前缀的表' ;
当然,这一切仍旧去客户现场进行证实
没有收集表的统计信息,SQL在生成执行计划时,该如何处理呢,我们知道,如果表上没有统计信息时,oracle会采用动态采样技术去获取表
及列和索引的统计信息,大家注意标红部分,表明采用了动态采样技术
|
Id
|
Operation
|
Name
|
Rows
|
Bytes
|
Cost (%CPU)
|
Time
|
|
0
|
SELECT STATEMENT
|
|
|
|
23 (100)
|
|
|
1
|
TABLE ACCESS FULL
|
BD_BANK_TYPE
|
1
|
17
|
3 (0)
|
00:00:01
|
|
2
|
SORT AGGREGATE
|
|
1
|
87
|
|
|
|
3
|
FILTER
|
|
|
|
|
|
|
4
|
TABLE ACCESS BY INDEX ROWID
|
TMP_ACCNT_BALANCE
|
115
|
10005
|
5 (0)
|
00:00:01
|
|
5
|
INDEX RANGE SCAN
|
IDX_TMP_BALANCE_DATE
|
53
|
|
1 (0)
|
00:00:01
|
|
6
|
VIEW
|
index$_join$_004
|
1
|
104
|
3 (34)
|
00:00:01
|
|
7
|
HASH JOIN
|
|
|
|
|
|
|
8
|
INDEX RANGE SCAN
|
IDX_PROC_ACC_NUM_L1
|
1
|
104
|
1 (0)
|
00:00:01
|
|
9
|
INDEX RANGE SCAN
|
IDX_PROC_ACC_NUM_L2
|
1
|
104
|
1 (0)
|
00:00:01
|
|
10
|
SORT AGGREGATE
|
|
1
|
74
|
|
|
|
11
|
FILTER
|
|
|
|
|
|
|
12
|
TABLE ACCESS BY INDEX ROWID
|
TMP_ACCNT_BALANCE
|
132
|
9768
|
5 (0)
|
00:00:01
|
|
13
|
INDEX RANGE SCAN
|
IDX_TMP_BALANCE_DATE
|
53
|
|
1 (0)
|
00:00:01
|
|
14
|
VIEW
|
index$_join$_006
|
1
|
104
|
3 (34)
|
00:00:01
|
|
15
|
HASH JOIN
|
|
|
|
|
|
|
16
|
INDEX RANGE SCAN
|
IDX_PROC_ACC_NUM_L1
|
1
|
104
|
1 (0)
|
00:00:01
|
|
17
|
INDEX RANGE SCAN
|
IDX_PROC_ACC_NUM_L3
|
1
|
104
|
1 (0)
|
00:00:01
|
|
18
|
SORT AGGREGATE
|
|
1
|
63
|
|
|
|
19
|
FILTER
|
|
|
|
|
|
|
20
|
HASH JOIN
|
|
997
|
62811
|
5441 (1)
|
00:01:06
|
|
21
|
TABLE ACCESS BY INDEX ROWID
|
RA_AD_OFFICE_PAY
|
998
|
50898
|
5256 (1)
|
00:01:04
|
|
22
|
INDEX RANGE SCAN
|
INDEX_BUSINESS_DATE
|
28890
|
|
104 (0)
|
00:00:02
|
|
23
|
INDEX FAST FULL SCAN
|
PK_RBBOI
|
177K
|
2082K
|
184 (1)
|
00:00:03
|
|
24
|
VIEW
|
index$_join$_009
|
1
|
104
|
3 (34)
|
00:00:01
|
|
25
|
HASH JOIN
|
|
|
|
|
|
|
26
|
INDEX RANGE SCAN
|
IDX_PROC_ACC_NUM_L1
|
1
|
104
|
1 (0)
|
00:00:01
|
|
27
|
INDEX RANGE SCAN
|
IDX_PROC_ACC_NUM_L4
|
1
|
104
|
1 (0)
|
00:00:01
|
|
28
|
VIEW
|
|
1
|
335
|
23 (18)
|
00:00:01
|
|
29
|
SORT ORDER BY
|
|
1
|
234
|
23 (18)
|
00:00:01
|
|
30
|
FILTER
|
|
|
|
|
|
|
31
|
NESTED LOOPS
|
|
3
|
702
|
22 (14)
|
00:00:01
|
|
32
|
VIEW
|
VW_NSO_2
|
23
|
299
|
19 (11)
|
00:00:01
|
|
33
|
HASH UNIQUE
|
|
1
|
897
|
|
|
|
34
|
NESTED LOOPS
|
|
23
|
897
|
19 (11)
|
00:00:01
|
|
35
|
VIEW
|
VW_NSO_1
|
2
|
26
|
11 (19)
|
00:00:01
|
|
36
|
SORT UNIQUE
|
|
2
|
44
|
11 (82)
|
00:00:01
|
|
37
|
UNION-ALL
|
|
|
|
|
|
|
38
|
FAST DUAL
|
|
1
|
|
2 (0)
|
00:00:01
|
|
39
|
TABLE ACCESS BY INDEX ROWID
|
RM_PARTY_RELATION
|
1
|
44
|
7 (0)
|
00:00:01
|
|
40
|
INDEX RANGE SCAN
|
IDX_PARTYREL_PARENTID_VIEW
|
7
|
|
3 (0)
|
00:00:01
|
|
41
|
TABLE ACCESS BY INDEX ROWID
|
TMP_FTS_ACC_REL
|
11
|
286
|
4 (0)
|
00:00:01
|
|
42
|
INDEX RANGE SCAN
|
IDX_PROC_MAIN_CORP_ID
|
11
|
|
1 (0)
|
00:00:01
|
|
43
|
TABLE ACCESS BY INDEX ROWID
|
TMP_ACCNT_BALANCE
|
3
|
663
|
2 (0)
|
00:00:01
|
|
44
|
INDEX RANGE SCAN
|
IDX_TMP_PK_ACTUAL_ACCNT
|
3
|
|
1 (0)
|
00:00:01
|
|
45
|
FILTER
|
|
|
|
|
|
|
46
|
HASH GROUP BY
|
|
1
|
44
|
2078 (1)
|
00:00:25
|
|
47
|
TABLE ACCESS FULL
|
RM_PARTY_RELATION
|
1761
|
77484
|
2077 (1)
|
00:00:25
|
.dynamic sampling used for this statement
摘录动态采样官方注解
何时使用动态采样
1, 对于查询速度很快的SQL,比如仅运行不到几秒,使用动态采样大可不必,反而增大成本,影响性能,
2, 但是,在如下情形下,采用动态采样是有益的:
? 使用动态采样可以选取更好的执行计划
? 动态采样的时间仅占更个SQL查询的很小一部分
? SQL查询会执行很多次
3, 动态采样既适用于单表谓词一部分,也适用于未采用动态采样的常规统计信息评估
如何使用动态采样提升数据库性能
通过参数optimizer_dynamic_samplingr控制动态采样的级别,值为0-10,默认为2
? 0表示禁用动态采样
? 值越大,动态采样越明显或者越强大,这个是基于采样表是否收集过统计信息以及采样消耗的IO资源
@ 如果采样表没有数据插入,删除或修改,动态采样依旧可以重复执行
@ 如果配置optimizer_features_enable=小于9.2.0,则禁用动态采样功能
动态采样的采样级别
l 动态采样级别可以通过参数optimizer_dynamic_sampling或SQL HINT进行配置
l optimizer_dynamic_sampling配置值如下:
? level 0:禁用动态采样
? level 1:如果满足如下条件,则采样所有没有被收集统计信息的表
SQL到少有1个表未采分析
未分析的表与另一个表进行关联或者出现在子查询
或不能 合并的视图
没有索引的未被分析的表
未被分析的表要比动态采样分析的表更多的数据块。默认 情况下,采样的数据块个数为32个数据块
level 2:对所有未分析的表进行动态采样,采样数据块个数是默认采样数据块个数的2倍
level 3:在level 2的基础上,加上用于评估潜在的动态采样谓词的所有表。动态采样数据块个数,默认采取动态采样默认的数据块个数,对于未被分析过的表,采样数据块个数是默认的2倍
level 4:在level 3的基础上,加上单表谓词有2个的所有表。其它同上
level 5,6,7,8,9:适应用于之前所有的level,分别以上述采样级别的2,4,8,32,128倍数进行动态采样数据块
level 10:满足level 9,且扫描使用表上所有的数据块,应用动态采样
动态采样在sql hint中应用,即dynamic_sampling:
level 0:同上
level 1:默认采样32个数块
level 2,3,4,5,6,7,8,9,同上,是默认动态采样数据块的2,4,8,16,32,64,128,256倍
level 10:动态采样会读取表所有的数据块
测试结论:
1,如果表DML进行DML或大批量数据加载,且处于自动收集统计信息job之外,是不会收集表相关统计信息
2,如果存储过程调用表,且表在存储过程中状态周期为创建,使用,删除,依旧不会收集表相关统计信息
(注:此存储过程运行时间要短,不能与自动收集统计信息重合)
3,通过dba_objects获取表是否频繁创建,存活周期很短
4,如果表没有收集统计信息,数据库会采用动态采样技术请及时收集表相关的统计信息
5,如果表没有收集统计信息,也可以通过如下脚本对比诊断,表统计信息收集的历史过程,此字典一般存储31天的数据,如果依旧没有信息,
原因可能是上述的几个原因之一
select operation,target,start_time,end_time
from dba_optstat_operations
where lower(target) like '%tmp前缀的表%';
6, 也可能是与相关的SQL刚首次运行,正好未在自动收集统计信息JOB之内运行,可以通过dba_hist_sql_stats进行诊断与确认