GATHERING STATISTICS FASTER FOR LARGE DATABASE ORACLE
There are scenarios where statistics need to be gathered after an import of database after ignoring statistics with datapump. It is suggested to use DBMS_STATS package rather than datapump to collect statistics faster for the newly imported objects. What if, the database is too large? Let us see some methods and case studies to observe the faster ways to collect statistics for large database.
Solution is “high resource” and nothing magic here
Check the size of the target schema for gathering statistics.
SQL> SELECT distinct ds.segment_name,ds.bytes/1048576 MB,dt.degree
FROM dba_Segments ds
INNER JOIN dba_tables dt on ( dt.owner = ds.owner )
WHERE
dt.owner='C##HYDRA'
ORDER BY MB desc;
Check the size of the database and top 5 big tables under the schema which contain big tables.
SQL> SELECT sum(bytes/1073741824) DB_GB from dba_segments;
DB_GB
----------
33.2966919
SQL> col SEGMENT_NAME for a20
SQL> SELECT * from (SELECT distinct ds.segment_name,ds.bytes/1048576 MB,dt.degree FROM dba_Segments ds INNER JOIN dba_tables dt on ( dt.owner = ds.owner ) ORDER BY MB desc) WHERE rownum < 6;
SEGMENT_NAME MB DEGREE
-------------------- ---------- ----------------------------------------
ADDRESSES 8422 1
ORDER_ITEMS 7350 1
CNS 4315.4375 1
CNS 4315.4375 4
UN 4287 1
To monitor the stats job, use the below query.
SQL> set lines 200
set pages 1000
col OPNAME format a20
col MESSAGE format a20
col username format a10
col opname format a10
col sid format 999999
col serial# format 999999
col TIME_REMAINING format 999999
col pct format 9999999
select sid,
serial#,
USERNAME,
opname,
100*(sofar/totalwork) as pct,
to_char(START_TIME,'dd-mm-yy hh24:mi:ss') as starttime,
to_char(LAST_UPDATE_TIME,'dd-mm-yy hh24:mi:ss') as change,
TIME_REMAINING,
ELAPSED_SECONDS,
MESSAGE
from v$session_longops
where sofar < totalwork
and message like '%Stat%';SQL> SQL> SQL> SQL> SQL> SQL> SQL> SQL> SQL> SQL> 2 3 4 5 6 7 8 9 10 11 12 13
SID SERIAL# USERNAME OPNAME PCT STARTTIME CHANGE TIME_REMAINING ELAPSED_SECONDS MESSAGE
------- ------- ---------- ---------- -------- ----------------- ----------------- -------------- --------------- --------------------
73 25839 SYS Gather Dat 47 01-07-23 14:15:53 01-07-23 14:36:35 1375 1242 Gather Database Stat
abase Stat istics: Database : 1
istics 095 out of 2307 Obje
cts done
73 25839 SYS Gather Tab 20 01-07-23 14:44:38 01-07-23 14:46:17 396 99 Gather Table's Index
le's Index Statistics: Table S
Statistic ALES : 1 out of 5 In
s dexes done
73 25839 SYS Gather Dat 47 01-07-23 14:47:47 01-07-23 15:09:44 1458 1317 Gather Database Stat
abase Stat istics: Database : 1
istics 095 out of 2307 Obje
cts done
Flush the shared pool and buffer cache and start the statistics job as a benchmark. Consider that this is the first time, the stats job is executed in the database and this time should be the initial baseline performance for stats job.
SQL> alter system flush shared_pool;
System altered.
SQL> alter system flush buffer_cache;
System altered.
SQL> SET TIMING ON
SQL> EXEC dbms_stats.gather_database_stats();
PL/SQL procedure successfully completed.
Elapsed: 00:46:11.69
It took 46 minutes for stats for whole database to complete.
Things to review are to check the configuration of parameters for stats preference.
- Notice degree and concurrent parameters mainly which are the influencers to improve performance.
- Also check for estimate_percent parameter which is related to the sample size of the stats gathering process. If this value is too low(5), then stats may be collected faster but with a trade off of less accuracy which means optimizer may generate suboptimal execution plans down the line. If the value is high(100), then the time taken may increase compared to low value as accurate samples are collected at granular level.
- If the table is partitioned, then check for INCREMENTAL and INCREMENTAL_LEVEL if they are disabled. Enable the parameter immediately as this may benefit 10x by avoiding the full table scan during partition statistics collection.
SQL> select 'AUTOSTATS_TARGET =====> '||DBMS_STATS.GET_PREFS('AUTOSTATS_TARGET')||'' as "STATS_PARAM" from dual
union all
select 'CASCADE =====> '||DBMS_STATS.GET_PREFS('CASCADE')||'' from dual
union all
select 'CONCURRENT =====> '||DBMS_STATS.GET_PREFS('CONCURRENT')||'' as "STATS_PARAM" from dual
union all
select 'DEGREE =====> '||DBMS_STATS.GET_PREFS('DEGREE')||'' from dual
union all
select 'ESTIMATE_PERCENT =====> '||DBMS_STATS.GET_PREFS('ESTIMATE_PERCENT')||'' from dual
union all
select 'GRANULARITY =====> '||DBMS_STATS.GET_PREFS('GRANULARITY')||'' from dual
union all
select 'INCREMENTAL =====> '||DBMS_STATS.GET_PREFS('INCREMENTAL')||'' from dual
union all
select 'INCREMENTAL_LEVEL =====> '||DBMS_STATS.GET_PREFS('INCREMENTAL_LEVEL')||'' from dual
union all
select 'INCREMENTAL_STALENESS =====> '||DBMS_STATS.GET_PREFS('INCREMENTAL_STALENESS')||'' from dual
union all
select 'METHOD_OPT =====> '||DBMS_STATS.GET_PREFS('METHOD_OPT')||'' from dual
union all
select 'OPTIONS =====> '||DBMS_STATS.GET_PREFS('OPTIONS')||'' from dual
union all
select 'STALE_PERCENT =====> '||DBMS_STATS.GET_PREFS('STALE_PERCENT')||'' from dual; 10 11 12 13 14 15 16 17 18 19 20 21 22 23
STATS_PARAM
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
AUTOSTATS_TARGET =====> AUTO
CASCADE =====> DBMS_STATS.AUTO_CASCADE
CONCURRENT =====> AUTO
DEGREE =====> NULL
ESTIMATE_PERCENT =====> DBMS_STATS.AUTO_SAMPLE_SIZE
GRANULARITY =====> AUTO
INCREMENTAL =====> FALSE
INCREMENTAL_LEVEL =====> PARTITION
INCREMENTAL_STALENESS =====>
METHOD_OPT =====> FOR ALL COLUMNS SIZE AUTO
OPTIONS =====> GATHER
STALE_PERCENT =====> 10
12 rows selected.
Delete statistics and regather stats with parallel servers. Do not allocate parallel for tables which are less than 1 GB in size which does not get benefitted from parallel.
SQL> EXEC dbms_stats.delete_database_stats();
PL/SQL procedure successfully completed.
SQL> alter system flush shared_pool;
System altered.
SQL> alter system flush buffer_cache;
System altered.
SQL> ALTER table c##hydra.ADDRESSES parallel 4;
Table altered.
SQL> ALTER table c##hydra.ORDER_ITEMS parallel 4;
Table altered.
SQL> ALTER table CNS parallel 4;
Table altered.
SQL> ALTER table c##hydra.ORDERS parallel 4;
Table altered.
SQL> ALTER table kish.sts parallel 2;
Table altered.
1.Check the settings of concurrent jobs which if set to manual, then concurrent jobs may use additional resources like CPU and MEMORY to further aggressively improve the performance but provided with more resource.
2. Make sure that the settigns for parallel max servers is correctly set as per the formula. ==> parallel_max_servers = cpu_count * parallel_threads_per_cpu * 5 * 2(if pga_aggregate_target parameter is set else 0)
3. Parallel degree policy plays a major role on how the parallel servers are controlled either manually or automatically. If set to manual then degree parameter is taken from table definition settings.(SELECT degree from dba_tables where table_name=<table>;) If set to auto, then parallel_degree_limit should be set to CPU so that oracle automatically control the parallel usage upto less than or equal to CPU threshold.
SQL> select dbms_stats.get_prefs('CONCURRENT') from dual;
DBMS_STATS.GET_PREFS('CONCURRENT')
--------------------------------------------------------------------------------
MANUAL
SQL> EXEC dbms_stats.set_global_prefs('DEGREE','DBMS_STATS.AUTO_DEGREE');
PL/SQL procedure successfully completed.
SQL> show parameter parallel_max
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
parallel_max_servers integer 100
SQL> ALTER system set parallel_max_servers=200;
System altered.
SQL> ALTER system set parallel_degree_policy=MANUAL;
System altered.
Other option is to configure resource manager and allocate majority of the resource to statistics jobs.
SQL> EXEC dbms_stats.gather_database_stats();
BEGIN dbms_stats.gather_database_stats(); END;
*
ERROR at line 1:
ORA-20000: Unable to gather statistics concurrently: Resource Manager is not
enabled.
ORA-06512: at "SYS.DBMS_STATS", line 36309
ORA-06512: at line 1
SQL> BEGIN
DBMS_RESOURCE_MANAGER.clear_pending_area;
DBMS_RESOURCE_MANAGER.create_pending_area;
-- Create plan
DBMS_RESOURCE_MANAGER.create_plan(
plan => 'hybrid_plan',
comment => 'Plan for a combination of high and low priority tasks.');
-- Create consumer groups
2 3 4 5 6 7 8 9 10 11 DBMS_RESOURCE_MANAGER.create_consumer_group(
consumer_group => 'Gather_stats',
comment => 'Gather_stats - high priority');
12 13 14
DBMS_RESOURCE_MANAGER.create_consumer_group(
consumer_group => 'Other',
comment => 'Other - low priority');
-- Assign consumer groups to plan and define priorities
DBMS_RESOURCE_MANAGER.create_plan_directive (
plan => 'hybrid_plan',
group_or_subplan => 'Gather_stats',
comment => 'High Priority - level 1',
cpu_p1 => 100,
cpu_p2 => 100,
15 16 17 18 19 20 21 22 23 24 25 cpu_p3 => 80);
DBMS_RESOURCE_MANAGER.create 26 _plan_directive (
plan => 'hybrid_plan',
group_or_subplan => 'Other',
comment => 'Low Priority - level 2',
cpu_p1 => 0,
cpu_p2 => 0,
cpu_p3 => 20);
27 28 29 30 31 32 33 34 35 36 DBMS_RESOURCE_MANAGER.validate_pending_area;
DBMS_RESOURCE_MANAGER.submit_pending_area;
END;
/ 37 38 39
BEGIN
*
ERROR at line 1:
ORA-65046: operation not allowed from outside a pluggable database
ORA-06512: at "SYS.DBMS_RMIN_SYS", line 1003
ORA-06512: at "SYS.DBMS_RESOURCE_MANAGER", line 114
ORA-06512: at line 6
SQL> DECLARE
x_plan VARCHAR2(30) := 'Gather_stats';
BEGIN
DBMS_RESOURCE_MANAGER.clear_pending_area;
DBMS_RESOURCE_MANAGER.create_pending_area;
DBMS_RESOURCE_MANAGER.create_cdb_plan(
plan => 'x_plan',
comment => 'Plan for statistics job');
DBMS_RESOURCE_MANAGER.create_cdb_plan_directive(
plan => 'x_plan',
pluggable_database => 'updb',
shares => 3,
utilization_limit => 100,
parallel_server_limit => 100);
DBMS_RESOURCE_MANAGER.validate_pending_area;
DBMS_RESOURCE_MANAGER.submit_pending_area;
END;
/ 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
PL/SQL procedure successfully completed.
Configure the resource plan and activate it so that the future jobs will use the plan for resource allocation.
SQL> show parameter resource
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
resource_limit boolean TRUE
resource_manager_cpu_allocation integer 4
resource_manager_plan string
SQL> ALTER system set resource_manager_plan=x_plan;
System altered.
Once the resource manager plan directive is configured, then monitor the system for a while on how the CPU process is distributed across PDB’s
top - 14:31:18 up 21 min, 2 users, load average: 12.31, 12.07, 8.83
Tasks: 308 total, 2 running, 306 sleeping, 0 stopped, 0 zombie
%Cpu(s): 14.6 us, 1.2 sy, 0.0 ni, 0.0 id, 83.1 wa, 0.9 hi, 0.1 si, 0.0 st
MiB Mem : 9670.4 total, 61.8 free, 1148.2 used, 8460.4 buff/cache
MiB Swap: 4096.0 total, 4082.9 free, 13.1 used. 2419.7 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
2405 oracle 20 0 6507416 510944 505968 D 16.9 5.2 0:29.23 ora_p005_hydrup
2318 oracle 20 0 6507072 574400 569196 D 15.6 5.8 0:28.31 ora_p001_hydrup
2401 oracle 20 0 6506920 503568 498504 D 6.3 5.1 0:30.86 ora_p003_hydrup
2320 oracle 20 0 6508108 555344 550468 D 5.3 5.6 0:31.49 ora_p002_hydrup
2409 oracle 20 0 6507176 462420 457508 D 5.3 4.7 0:28.64 ora_p007_hydrup
2407 oracle 20 0 6507448 528824 523288 D 4.3 5.3 0:31.62 ora_p006_hydrup
2403 oracle 20 0 6507180 503548 498812 D 3.0 5.1 0:29.91 ora_p004_hydrup
Check the long running stats job from v$session_longops to get an idea on the progress.
SQL> SELECT TARGET,STATUS,JOB_NAME,NOTES from dba_optstat_operations WHERE START_TIME > SYSDATE - (1/24);
TARGET STATUS JOB_NAME NOTES
-------------------- -------------------- -------------------- --------------------
COMPLETED <params><param name=
"force" val="FALSE"/
><param name="no_inv
alidate" val="NULL"/
><param name="statid
" val=""/><param nam
e="statown" val=""/>
<param name="stattab
" val=""/><param nam
e="stattype" val="AL
L"/></params>
IN PROGRESS <params><param name=
"block_sample" val="
FALSE"/><param name=
"cascade" val="NULL"
/><param name="concu
rrent" val="TRUE"/><
param name="degree"
val="DEFAULT_DEGREE_
VALUE"/><param name=
"estimate_percent" v
al="DEFAULT_ESTIMATE
_PERCENT"/><param na
me="gather_fixed" va
l="FALSE"/><param na
me="gather_sys" val=
"TRUE"/><param name=
"gather_temp" val="F
ALSE"/><param name="
granularity" val="DE
FAULT_GRANULARITY"/>
<param name="method_
opt" val="DEFAULT_ME
THOD_OPT"/><param na
me="no_invalidate" v
al="NULL"/><param na
me="options" val="GA
THER"/><param name="
reporting_mode" val=
"FALSE"/><param name
="statid" val=""/><p
aram name="statown"
val=""/><param name=
"stattab" val=""/><p
aram name="stattype"
val="DATA"/></param
s>
Now execute the stats for the second time with parallel settings updated. It took 54 minutes to complete with parallel settings for specific big tables. However, this has not improved the overall performance of the database statistics.
SQL> EXEC dbms_stats.gather_database_stats();
PL/SQL procedure successfully completed.
Elapsed: 00:54:28.14
Check the existing plan directives for the resource plan X_PLAN which was configured before.
SQL> SELECT PLAN,PLUGGABLE_DATABASE "PDB",SHARES,UTILIZATION_LIMIT,PARALLEL_SERVER_LIMIT,MEMORY_LIMIT,COMMENTS from dba_cdb_rsrc_plan_directives WHERE PLAN='X_PLAN';
PLAN PDB SHARES UTILIZATION_LIMIT PARALLEL_SERVER_LIMIT MEMORY_LIMIT COMMENTS
------------------------------ -------------------- ---------- ----------------- --------------------- ------------ --------------------
X_PLAN ORA$AUTOTASK 90 100 Autotask Directive
X_PLAN UPDB 3 100 100
X_PLAN ORA$DEFAULT_PDB_DIRE 1 100 100 Default Directive
CTIVE
Allocate all the parallel resource upto 100% to the PDB which contain the big tables.
SQL> DECLARE
l_plan VARCHAR2(30) := 'Gather_stats';
BEGIN
DBMS_RESOURCE_MANAGER.clear_pending_area;
DBMS_RESOURCE_MANAGER.create_pending_area;
DBMS_RESOURCE_MANAGER.create_cdb_plan(
plan => 'x3_plan',
comment => 'Plan for statistics job');
DBMS_RESOURCE_MANAGER.create_cdb_plan_directive(
plan => 'x3_plan',
pluggable_database => 'updb',
shares => 100,
utilization_limit => 100,
parallel_server_limit => 100);
DBMS_RESOURCE_MANAGER.validate_pending_area;
DBMS_RESOURCE_MANAGER.submit_pending_area;
END;
/ 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
PL/SQL procedure successfully completed.
Validate if all the parallel processes are utilized after allocating 100% parallel to updb
SQL> SELECT count(*) FROM v$px_process WHERE status <> 'AVAILABLE';
COUNT(*)
----------
56
It took 43 minutes after allocating 100% resource to updb which contain huge tables
SQL> set timing on
SQL> EXEC dbms_stats.gather_database_stats();
PL/SQL procedure successfully completed.
Elapsed: 00:43:42.06
When we enable restricted session to suppress other activities in the database,
Never ending process due to restricted session. Because parallel slaves are not used for stats gathering.
SQL> SET TIMING ON
SQL> ALTER system enable restricted session;
System altered.
SQL> EXEC dbms_stats.gather_database_stats();
^Z^CBEGIN dbms_stats.gather_database_stats(); END;
*
ERROR at line 1:
ORA-01013: user requested cancel of current operation
ORA-06512: at "SYS.DBMS_AQ", line 366
ORA-06512: at "SYS.DBMS_STATS", line 5710
ORA-06512: at "SYS.DBMS_STATS", line 6594
ORA-06512: at "SYS.DBMS_STATS", line 34846
ORA-06512: at "SYS.DBMS_STATS", line 35711
ORA-06512: at "SYS.DBMS_STATS", line 36230
ORA-06512: at "SYS.DBMS_STATS", line 36301
ORA-06512: at line 1
Elapsed: 03:14:49.46
For aggressive speed of the stats gathering, set the following if more CPU is available for oracle database.
Warning: Always test the parameters and never blindly set CPU and parallel thread parameters in a production system.
[oracle@hydrupgrd ~]$ lscpu|grep 'CPU(s):'|grep -v 'NUMA'
CPU(s): 4
SQL> show parameter cpu
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
cpu_count integer 1
parallel_threads_per_cpu integer 1
resource_manager_cpu_allocation integer 4
SQL> EXEC dbms_stats.set_global_prefs('CONCURRENT','TRUE');
PL/SQL procedure successfully completed.
SQL> ALTER system set cpu_count=4;
System altered.
SQL> ALTER system set parallel_threads_per_cpu=2;
System altered.
SQL> ALTER system set parallel_max_servers=80;
System altered.
SQL> ALTER system set parallel_servers_target=60;
System altered.
SQL> ALTER system set job_queue_processes=200;
System altered.
SQL> ALTER system set resource_manager_plan=x_plan;
System altered.
SQL> ALTER system set parallel_execution_message_size=32768 scope=spfile;
System altered.
SQL> startup force;
ORACLE instance started.
Total System Global Area 637534208 bytes
Fixed Size 2927624 bytes
Variable Size 494928888 bytes
Database Buffers 134217728 bytes
Redo Buffers 5459968 bytes
Database mounted.
Database opened.
Execute the stats job after changes and check the load on the server.
top – 14:22:46 up 3:29, 2 users, load average: 7.39, 5.87, 6.49
Tasks: 481 total, 1 running, 479 sleeping, 1 stopped, 0 zombie
%Cpu(s): 4.9 us, 0.4 sy, 0.0 ni, 28.7 id, 65.4 wa, 0.6 hi, 0.0 si, 0.0 st
MiB Mem : 9670.4 total, 72.0 free, 1895.5 used, 7702.9 buff/cache
MiB Swap: 4096.0 total, 4087.0 free, 9.0 used. 7333.9 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
16455 oracle 20 0 1002628 247140 231664 S 11.9 2.5 0:06.72 ora_j027_hydrup
16052 oracle 20 0 1058584 295500 251372 S 2.6 3.0 0:04.98 oracle_16052_hy
16213 oracle 20 0 1002536 240012 230148 S 2.3 2.4 0:04.90 ora_j164_hydrup
15121 oracle 20 0 992500 249032 243396 S 1.0 2.5 0:07.52 ora_j004_hydrup
14809 oracle -2 0 985180 50068 48056 S 0.7 0.5 0:10.80 ora_vktm_hydrup
14917 oracle 20 0 987120 188932 185096 D 0.7 1.9 0:06.50 ora_p003_hydrup
16983 oracle 20 0 65872 5384 4212 R 0.7 0.1 0:00.32 top
113 root 0 -20 0 0 0 I 0.3 0.0 0:01.30 kworker/3:1H-kblockd
14815 oracle 20 0 985180 174148 172108 S 0.3 1.8 0:00.32 ora_mman_hydrup
14823 oracle 20 0 985180 57620 55580 S 0.3 0.6 0:01.95 ora_vkrm_hydrup
14861 oracle 20 0 989660 180228 176572 S 0.3 1.8 0:03.39 ora_p001_hydrup
14863 oracle 20 0 987780 195612 191312 D 0.3 2.0 0:05.33 ora_p002_hydrup
15470 oracle 20 0 988032 134944 131152 S 0.3 1.4 0:00.05 ora_j068_hydrup
15778 oracle 20 0 988152 179724 175272 S 0.3 1.8 0:00.37 ora_j221_hydrup
15886 oracle 20 0 987104 201764 197664 S 0.3 2.0 0:00.85 ora_j066_hydrup
16647 oracle 20 0 987008 162324 158608 S 0.3 1.6 0:00.34 ora_j242_hydrup
16952 oracle 20 0 1002712 263716 246444 D 0.3 2.7 0:07.07 ora_j002_hydrup
Check CPU usage now.
SQL> set lines 250
set pages 2000
col username format a15 justify center
col program format a20 justify center
col event format a20 justify center
col sid format 999999 justify center
col CPU_USAGE format 999999999 justify center
col CPUPCT format 9999999999 justify center
col value format 999999
SQL> SQL> SQL> SQL> SQL> SQL> SQL> SQL> SQL> col PCT format 999999.99
with "CPUPCT" as (select round(sum(value),3) as total_cpu from v$sesstat)
select * from (select z.sid,z.status,nvl(z.username,'oracle-bg') as username,nvl(z.SQL_ID,'non-SQL') as SQL_ID,z.EVENT,z.program,
SQL> 2 3 round(sum(y.value)/100,6) as "CPU_USAGE",round(trunc(100*round(sum(y.value/100)),3)/(select * from CPUPCT),3) as PCT
from v$statname x
4 5 inner join v$sesstat y on x.STATISTIC# = y.STATISTIC#
inner join v$session z on y.SID = z.SID
6 7 where x.name in ('CPU used by this session')
group by z.sid,z.username,z.SQL_ID,z.EVENT,z.program,z.status order by CPU_USAGE desc)
where rownum < 6; 8 9
SID STATUS USERNAME SQL_ID EVENT PROGRAM CPU_USAGE PCT
------- -------- --------------- ------------- -------------------- -------------------- ---------- ----------
161 ACTIVE SYS 6hwah6nnt0dhd db file sequential r oracle@hydrupgrd (J0 11 .00
ead 27)
11 ACTIVE oracle-bg non-SQL rdbms ipc message oracle@hydrupgrd (CJ 8 .00
Q0)
554 ACTIVE SYS 4xg0vm951zzpx db file sequential r sqlplus@hydrupgrd (T 6 .00
ead NS V1-V3)
146 ACTIVE oracle-bg non-SQL rdbms ipc message oracle@hydrupgrd (MM 3 .00
ON)
308 ACTIVE oracle-bg non-SQL EMON slave idle wait oracle@hydrupgrd (Q0 3 .00
0E)
SQL> /
SID STATUS USERNAME SQL_ID EVENT PROGRAM CPU_USAGE PCT
------- -------- --------------- ------------- -------------------- -------------------- ---------- ----------
161 ACTIVE SYS 5jw2nd7s3kda0 PX Deq: Execute Repl oracle@hydrupgrd (J0 13 .00
y 27)
11 ACTIVE oracle-bg non-SQL rdbms ipc message oracle@hydrupgrd (CJ 8 .00
Q0)
554 ACTIVE SYS 4xg0vm951zzpx db file sequential r sqlplus@hydrupgrd (T 7 .00
ead NS V1-V3)
146 ACTIVE oracle-bg non-SQL rdbms ipc message oracle@hydrupgrd (MM 3 .00
ON)
308 ACTIVE oracle-bg non-SQL EMON slave idle wait oracle@hydrupgrd (Q0 3 .00
0E)
After enabling concurrent, increasing max parallel, job queues, pems, cpu count etc…., it took 19 minutes for the statistics to complete which is almost 60% improvement.
SQL> set timing on
SQL> set time on
16:11:34 SQL> EXEC dbms_stats.gather_database_stats();
PL/SQL procedure successfully completed.
Elapsed: 00:19:33.79
Another cause effective way to focus on the exact problem is to trace the stats job and find out the problematic table where the time is spent all the time using stats trace.
SQL> select dbms_stats.get_param('trace') from dual;
DBMS_STATS.GET_PARAM('TRACE')
--------------------------------------------------------------------------------
0
SQL> exec DBMS_STATS.SET_GLOBAL_PREFS('trace', 2+4+8+16+64+512+1024+2048 /* 3678 */);
PL/SQL procedure successfully completed.
SQL> select dbms_stats.get_param('trace') from dual;
DBMS_STATS.GET_PARAM('TRACE')
--------------------------------------------------------------------------------
3676
*** 2023-11-20 10:43:25.700
DBMS_STATS: gather_database_stats: <params><param name="block_sample" val="FALSE"/><param name="cascade" val="NULL"/><param name="concurrent" val="TRUE"/><param name="degree" val="DEFAULT_DEGREE_VALUE"/><param name="estimate_percent" val="DEFAULT_ESTIMATE_PERCENT"/><param name="gather_fixed" val="FALSE"/><param name="gather_sys" val="TRUE"/><param name="gather_temp" val="FALSE"/><param name="granularity" val="DEFAULT_GRANULARITY"/><param name="method_opt" val="DEFAULT_METHOD_OPT"/><param name="no_invalidate" val="NULL"/><param name="options" val="GATHER"/><param name="reporting_mode" val="FALSE"/><param name="statid" val=""/><param name="statown" val=""/><param name="stattab" val=""/><param name="stattype" val="DATA"/></params>
With a combination of stats trace and 10046 trace, more data can be collected with additional hints.
The below information shows that the stats job mostly waited on Streams AQ: waiting for messages in the queue wait event which is advanced queueing issue. This may not be the same case for every one who encounter stats slowness as every system is different. This may be a bug related to concurrent settings with DBMS_STATS. Disabling concurrent may reduce the events but may probably increase the time further if there is another potential problem because of non concurrent jobs.
SQL ID: 44pgaj5vdubxy Plan Hash: 0
BEGIN dbms_stats.gather_database_stats(); END;
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 3.51 11156.78 734 1484 934 0
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 3.51 11156.78 734 1484 934 0
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYS
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
Disk file operations I/O 4 0.00 0.00
db file sequential read 721 0.34 6.75
control file sequential read 600 0.00 0.00
latch: shared pool 1 0.00 0.00
Streams AQ: waiting for messages in the queue
1106 12.53 11146.59
SQL*Net break/reset to client 1 0.00 0.00
SQL*Net message to client 1 0.00 0.00
OVERALL TOTALS FOR ALL NON-RECURSIVE STATEMENTS
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 3.51 11156.78 734 1484 934 0
Fetch 0 0.00 0.00 0 0 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 2 3.51 11156.78 734 1484 934 0
Misses in library cache during parse: 1
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 2 0.00 0.00
SQL*Net message from client 1 39.74 39.74
Disk file operations I/O 4 0.00 0.00
db file sequential read 750 0.34 6.78
control file sequential read 600 0.00 0.00
latch: shared pool 1 0.00 0.00
Streams AQ: waiting for messages in the queue
1106 12.53 11146.59
SQL*Net break/reset to client 1 0.00 0.00