Professional Documents
Culture Documents
Optimizer Statistics: Julian Dyke Independent Consultant
Optimizer Statistics: Julian Dyke Independent Consultant
Optimizer Statistics: Julian Dyke Independent Consultant
Julian Dyke
Independent Consultant
Web Version
1 © 2010 Julian Dyke juliandyke.com
Agenda
Statistics Strategies
ANALYZE
DBMS_STATS
ANALYZE versus DBMS_STATS
System Statistics
Automatic Statistics Collection
Statistics History
Manual Statistics
Partition Statistics
Oracle 11g Enhancements
Advantages
Online analyze - Oracle 9.0 and above
Optionally validates structure of tables, indexes and clusters
Optionally detects chained / migrated rows
Disadvantages
Serial statistics collection only
Limited partition-awareness
ONLINE option
only checks structure
does not require TM (DML) lock
does not populate INDEX_STATS or INDEX_HISTOGRAM
Defined in $ORACLE_HOME/rdbms/admin/dbmsstat.sql
Advantages
Parallel statistics collection
Partition-awareness
Disadvantages
Cannot perform structure validation
Cannot detect chaining / migration
DELETE_TABLE_STATS includes
CASCADE_PART - optionally delete partition statistics
CASCADE_COLUMNS - optionally delete column statistics
CASCADE_INDEXES - optionally delete index statistics
dbms_stats.create_stats_table
(
ownname => 'SYSTEM',
tabname => 'STATS1',
tblspace=> 'SYSAUX'
);
To transfer the statistics to another database export and import the statistics
table
The OBJLIST parameter must be specified as an OUT parameter for the LIST
options
GATHER STALE and GATHER EMPTY can also return a list of objects if
this parameter is specified
DBMS_STATS.ALTER_SCHEMA_TAB_MONITORING ('<owner>',TRUE);
DBMS_STATS.ALTER_DATABASE_TAB_MONITORING (TRUE);
Reported in DBA_TABLES.MONITORING
In Oracle 10.2 the default value can be changed using SET_PARAM procedure
Subroutines are:
GATHER_DICTIONARY_STATS
DELETE_DICTIONARY_STATS
EXPORT_DICTONARY_STATS
RESTORE_DICTIONARY_STATS
Subroutines include:
GATHER_FIXED_OBJECTS_STATS
DELETE_FIXED_OBJECTS_STATS
EXPORT_FIXED_OBJECT_STATS
RESTORE_FIXED_OBJECTS_STATS
For example:
dbms_stats.gather_fixed_objects_stats;
Only gather fixed object statistics after the database has been running a
representative workload
where:
integer - number of histogram buckets (1..254)
REPEAT - only collect histograms for columns that already have
histograms
AUTO - determine which columns need histograms automatically
SKEWONLY - determine which columns need histograms based on data
distribution
Height-balanced histograms
Limited use if column is not highly skewed
dbms_stats.lock_table_stats
(
ownname => 'USER1',
tabname => 'T1',
stattype => 'ALL'
);
Possible values for STATTYPE are NULL, DATA, CACHE and ALL
AUTOSTATS_TARGET can be
ALL collect statistics for all objects in the database
ORACLE collect statistics for all Oracle-owned objects
AUTO determine which objects need new statistics
dbms_stats.set_param ('ESTIMATE_PERCENT',NULL);
Restores factory default (DBMS_STATS.AUTO_SAMPLE_SIZE)
dbms_stats.reset_param_defaults;
Tables
AVG_ROW_LEN differs - e.g.:
ANALYZE 43
DBMS_STATS 39
Indexes
No obvious differences between ANALYZE and DBMS_STATS
Columns
AVG_COL_LEN differs - e.g.:
ANALYZE 4
DBMS_STATS 5
Use DBMS_STATS
Gather statistics on tables / indexes
Subroutines are:
GATHER_SYSTEM_STATS
DELETE_SYSTEM_STATS
GET_SYSTEM_STATS
SET_SYSTEM_STATS
EXPORT_SYSTEM_STATS
IMPORT_SYSTEM_STATS
RESTORE_SYSTEM_STATS
dbms_stats.create_stat_table ('SYS','OLTP_STATS');
Gather system statistics for a typical period using:
dbms_stats.gather_system_stats
(
gathering_mode => 'INTERVAL',
interval => 60, -- 60 seconds
stattab => OLTP_STATS',
statid => 'OLTP'
);
Import system statistics into AUX_STATS$ using:
dbms_stats.import_system_stats
(
stattab => OLTP_STATS',
statid => 'OLTP',
statown => 'SYS'
);
Scheduler job
GATHER_STATS_JOB
Scheduler job class
AUTO_TASKS_JOB_CLASS
Scheduler windows
WEEKNIGHT_WINDOW
WEEKEND_WINDOW
Scheduler window group
MAINTENANCE_WINDOW_GROUP
SELECT stats_update_time
FROM dba_tab_stats_history
WHERE owner = 'USER1'
AND table_name = 'T1';
12-FEB-09 04.36.32.997000 PM +00:00
DBA_OPTSTAT_OPERATIONS includes:
gather_database_stats(auto)
gather_schema_stats
SELECT dbms_stats.get_stats_history_retention
FROM dual;
Default is 31 days
To check earliest historic statistics use:
SELECT dbms_stats.get_stats_history_availability
FROM dual;
12-JAN-09 11.17.50.176000000 PM +00:00
dbms_stats.alter_stats_history_retention
(
retention => 90
);
DECLARE
l_numrows NUMBER;
l_numblks NUMBER;
l_avgrlen NUMBER;
l_flags NUMBER;
l_total_blocks NUMBER;
l_total_bytes NUMBER;
l_unused_blocks NUMBER;
l_unused_bytes NUMBER;
l_last_used_extent_file_id NUMBER;
l_last_used_extent_block_id NUMBER;
l_last_used_block NUMBER;
BEGIN
dbms_stats.get_table_stats
(
ownname => 'GP',
tabname => 'CAR',
numrows => l_numrows,
numblks => l_numblks,
avgrlen => l_avgrlen,
);
l_numrows := 0;
l_numblks := 0;
SELECT COUNT(*) INTO l_numrows FROM gp.car;
dbms_space.unused_space
(
segment_owner => 'GP',
segment_name => 'CAR',
segment_type => 'TABLE',
total_blocks => l_total_blocks,
total_bytes => l_total_bytes,
unused_blocks => l_unused_blocks,
unused_bytes => l_unused_bytes,
last_used_extent_file_id => l_last_used_extent_file_id,
last_used_extent_block_id => l_last_used_extent_block_id,
last_used_block => l_last_used_block
);
l_numblks := l_total_blocks;
dbms_stats.set_table_stats
(
ownname => 'GP',
tabname => 'CAR',
numrows => l_numrows,
numblks => l_numblks,
avgrlen => l_avgrlen
);
END;
DBMS_STATS.SET_COLUMN_STATS
(
ownname => p_owner,
tabname => p_table_name,
colname => l_row.column_name,
distcnt => l_row.num_distinct,
density => l_row.density,
nullcnt => l_row.num_nulls,
srec => l_statrec,
avgclen => l_row.avg_col_len
);
END IF;
END LOOP;
CLOSE c1;
END;
dbms_stats.gather_table_stats
(
ownname => 'GP',
tabname => 'CAR3',
estimate_percent => NULL,
cascade => TRUE
);
65 © 2010 Julian Dyke juliandyke.com
Partition Statistics
Example (2 of 12)
Table Statistics
SELECT num_rows,blocks,avg_row_len
FROM dba_tables
WHERE owner = 'GP' AND table_name = 'CAR3';
SELECT partition_name,num_rows,blocks,avg_row_len
FROM dba_tab_partitions
WHERE table_owner = 'GP' AND table_name = 'CAR3';
SELECT partition_name,
blevel, leaf_blocks, distinct_keys, clustering_factor, num_rows
FROM dba_ind_partitions
WHERE index_owner = 'GP' AND index_name = 'CAR3_I1';
dbms_stats.gather_table_stats
(
ownname => 'GP',
tabname => 'CAR3',
estimate_percent => NULL,
cascade => TRUE
);
SELECT num_rows,blocks,avg_row_len
FROM dba_tables
WHERE owner = 'GP' AND table_name = 'CAR3';
SELECT partition_name,num_rows,blocks,avg_row_len
FROM dba_tab_partitions
WHERE table_owner = 'GP' AND table_name = 'CAR3';
SELECT partition_name,
blevel, leaf_blocks, distinct_keys, clustering_factor, num_rows
FROM dba_ind_partitions
WHERE index_owner = 'GP' AND index_name = 'CAR3_I1';
dbms_stats.copy_table_stats
(
ownname => 'GP',
tabname => 'CAR3',
srcpartname => 'P2008',
dstpartname => 'P2009'
);
SELECT num_rows,blocks,avg_row_len
FROM dba_tables
WHERE owner = 'GP' AND table_name = 'CAR3';
SELECT partition_name,num_rows,blocks,avg_row_len
FROM dba_tab_partitions
WHERE table_owner = 'GP' AND table_name = 'CAR3';
SELECT partition_name,
blevel, leaf_blocks, distinct_keys, clustering_factor, num_rows
FROM dba_ind_partitions
WHERE index_owner = 'GP' AND index_name = 'CAR3_I1';
0 SELECT STATEMENT
1 0 SORT AGGREGATE
2 1 PARTITION RANGE (SINGLE)
3 2 TABLE ACCESS (FULL) OF 'CAR3'
0 SELECT STATEMENT
1 0 SORT AGGREGATE
2 1 PARTITION RANGE (SINGLE)
3 2 TABLE ACCESS (BY INDEX ROWID) OF 'CAR3'
4 3 INDEX (RANGE SCAN) OF 'CAR3_I1'
Plans are different even though statistics and data are theoretically identical
SELECT partition_name,low_value,high_value
FROM dba_part_col_statistics
WHERE owner = 'GP'
AND table_name = 'CAR3'
AND column_name = 'SEASON_KEY';
dbms_stats.publish_pending_statistics
(
ownname => 'USER1',
tabname => NULL
);
Multicolumn statistics
Appear to work for frequency histograms
<= 254 combinations of values
Do not appear to work for height-based histograms
> 254 combinations of values
BEGIN
dbms_stats.gather_table_stats
(
ownname => 'GP',
tabname => 'CAR',
estimate_percent => NULL,
method_opt => 'FOR ALL COLUMNS SIZE 254 '||
'FOR COLUMNS (TEAM_KEY,ENGINE_KEY) SIZE 254'
);
END;
90 © 2010 Julian Dyke juliandyke.com
Oracle 11g Enhancements
Multi-Column Statistics
BEGIN
dbms_stats.gather_table_stats
(
ownname => 'GP',
tabname => 'CAR',
estimate_percent => NULL,
method_opt => 'FOR ALL COLUMNS SIZE 254 '||
'FOR COLUMNS (points(season_key,race_key,position)) SIZE 254'
);
END;
96 © 2010 Julian Dyke juliandyke.com
Oracle 11g Enhancements
Expression Statistics
SELECT COUNT(*) FROM gp.car
WHERE POINTS (season_key,race_key,position) = 10;
COUNT(*)
709
Prior to Oracle 11.1 calculation of global statistics required full table scan of
each partition in table
For many applications using range partitioning only the most recent partition
is subject to change
Older partitions contain historical data
DBMS_STATS.GATHER_TABLE_STATS
(
ownname => 'GP',
tabname => 'CAR4'
);
DBMS_STATS.GATHER_TABLE_STATS
(
ownname => 'GP',
tabname => 'CAR4'
);
If a partition has not been modified synopsis can be used to calculate global
statistics
Synopsis must be generated for all partitions first time statistics are gathered
after incremental statistics are enabled
BEGIN
DBMS_STATS.SET_TABLE_PREFS
(
ownname => 'GP',
tabname => 'CAR4',
pname => 'INCREMENTAL',
pvalue => 'TRUE'
);
END;
/
DBMS_STATS.GATHER_TABLE_STATS
( All partitions will
ownname => 'GP', be scanned first
tabname => 'CAR4' time to create
); synopsis
DBMS_STATS.GATHER_TABLE_STATS
(
ownname => 'GP',
tabname => 'CARP'
);
2006 Synopsis
2007 Synopsis
2008 Synopsis
2009 Full Table Scan
Greg Rahn
Tony Hasler
info@juliandyke.com