数据库性能优化
相关书籍:
1.基于成本的Oracle优化法则
2.Oracle性能诊断艺术
3.基于Oracle的SQL优化
-----------------------------------------------------------------------------------------
两种优化器:
CBO cost-base optimizer
基于cost 更大适应性/灵活性/10g开始
RBO rule-base optimizer
基于规则 制定了15条/10g以前
-------------------------------------------------------------------------------------------
统计信息:
1.系统统计信息
cpu、I/O
exec dbms_stats.gather_system_stats;
2.数据库对象统计信息
表、索引、列、扩展
exec dbms_stats.gather_table_stats('TEST_USER1','TEST_1');
--收集表的统计信息(同时会级联收集索引和列的统计信息)
exec dbms_stats.gather_table_stats('TEST_USER1','TEST_1',cascade=>false);
--不会联机收集索引和列的统计信息
exec dbms_stats.gather_index_stats('TEST_USER1','INDEX_NAME');
--收集索引的统计信息
alter table table_name enable row movement;
--允许表的行迁移
alter table table_name shrink space;
--降低表的高水位线
exec dbms_stats.gather_database_stats;
--收集整个数据库的统计信息
-------------------------------------------------------------------------------------------
直方图
扩展统计信息
桶
query rewrite 查询重写
星形转换
视图合并
为此前推
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
1,统计信息概述
统计信息是oracle CBO计算候选执行计划的cost的基础。因此,获取准确的统计信息,
是oracle能够选择最优执行计划(cost最低)的首要条件。
在oracle 9i的时候,oracle只计算每个执行计划的I/O成本,并将其作为评价执行计划好坏的依据。
从10g开始,oracle将sql执行中消耗的cpu资源,也作为成本计入执行计划的成本计算。
因此,一个执行计划的cost值,包含了cpu和I/O两种资源的成本。
与统计信息相关的几个初始化参数
SQL> show parameter statistics;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
optimizer_use_pending_statistics boolean FALSE
statistics_level string TYPICAL
timed_os_statistics integer 0
timed_statistics boolean TRUE
optimizer_use_pending_statistics:
显示CBO是否使用pending状态的统计信息。
一般情况下,我们收集完统计信息,则oracle会默认将新收集到的统计信息置于publish状态,这样CBO可以随时使用。
我们可以使用dbms_stats.DELETE_PENDING_STATS删除处于pending状态的统计信息,也可以使用
dbms_stat.set_*_prefs来设置统计信息是否处于publish状态。
timed_statistics
是否收集与时间相关的统计信息。例如cpu和elapsed times。
如果STATISTICS_LEVEL设置为TYPICAL或者ALL, 则该参数的默认值为true;
如果STATISTICS_LEVEL设置为BASIC, 则该参数的默认值为false。
从11.1.0.7.0开始,如果STATISTICS_LEVEL设置为TYPICAL或者ALL,该参数不能被设置为false。
timed_os_statistics
该参数指定了当从客户端生成一个请求,或者完成一个请求的时候,收集系统统计信息的时间间隔(单位为s)。这又分了两种情况:
dedicated 模式
在用户登录和后继客户端通过OCI调用从而作为一个远程过程调用信息进入oracle server时,oracle收集系统统计信息。
shared 模式
在客户端调用oracle完成时,oracle收集系统统计信息。
如果STATISTICS_LEVEL设置为ALL, 则该参数默认值为5;
如果STATISTICS_LEVEL设置为BASIC或者TYPICAL, 则该参数默认值为0。
需要注意的是,收集系统统计信息是相当消耗系统资源的活动。oracle强烈建议你在完成系统统计信息收集之后,立即将该参数修改为0。
statistics_level
控制收集统计信息的级别,有三个参数值:
BASIC :收集基本的统计信息
TYPICAL:收集大部分统计信息(数据库的默认设置)
ALL:收集全部统计信息,除了收集typical模式下的所有统计信息之外,还将收集基于时间的os统计信息和执行计划的统计信息。
(执行计划的统计信息包括什么)
如果将默认值typical改为basic,则将停止收集如下对象的统计信息:
Automatic Workload Repository (AWR) Snapshots
Automatic Database Diagnostic Monitor (ADDM)
All server-generated alerts
Automatic SGA Memory Management
Automatic optimizer statistics collection
Object level statistics
End to End Application Tracing (V$CLIENT_STATS)
Database time distribution statistics (V$SESS_TIME_MODEL and V$SYS_TIME_MODEL)
Service level statistics
Buffer cache advisory
MTTR advisory
Shared pool sizing advisory
Segment level statistics
PGA Target advisory
Timed statistics
Monitoring of statistics
可以用如下sql来查看oracle目前收集哪些对象的统计信息:
select * from v$statistics_level
--------------------------------------------------------------------------------------------------------------------------------------
1.1 系统统计信息
除了收集数据库中所有对象的统计信息以外,oracle也可以收集数据库所在主机的统计信息,也就是os级别的统计信息。
这包括主机的cpu性能(频率),系统I/O的吞吐量等。但是,在默认情况下,oracle不会自动的收集系统统计信息。
因此,当完成数据库在服务器主机上的安装配置之后,可以考虑手动收集系统级别的统计信息。
另外,在数据库主机进行硬件升级之后,尤其是是I/O系统进行了重大升级之后,也应该再次手动收集系统级别的统计信息。
可以用如下的命令来手工完成系统统计信息的收集:
SQL> exec dbms_stats.gather_system_stats;
PL/SQL procedure successfully completed
SQL> select * from sys.aux_stats$;
SNAME PNAME PVAL1 PVAL2
--------------------------------------------------------------------------------------------
SYSSTATS_INFO STATUS COMPLETED
SYSSTATS_INFO DSTART 06-13-2014 16:52
SYSSTATS_INFO DSTOP 06-13-2014 16:52
SYSSTATS_INFO FLAGS 1
SYSSTATS_MAIN CPUSPEEDNW 1504
SYSSTATS_MAIN IOSEEKTIM 2
SYSSTATS_MAIN IOTFRSPEED 4096
SYSSTATS_MAIN SREADTIM
SYSSTATS_MAIN MREADTIM
SYSSTATS_MAIN CPUSPEED
SYSSTATS_MAIN MBRC
SYSSTATS_MAIN MAXTHR
SYSSTATS_MAIN SLAVETHR
13 rows selected
该过程中的gathering_mode参数可以用来指定收集系统统计信息的模式:
NOWORKLOAD: 无工作负载模式,用于收集系统I/O部分的统计信息。
INTERVAL: 收集某一指定时间间隔内的统计信息。
START | STOP:手动收集一段时间内的统计信息。
在12c中,又引入了EXADATA模式,用来收集类似于exadata这样具有高I/O吞吐量和吞吐能力的系统统计信息。
注:关于该过程所有参数的详细描述,请参考Oracle Database PL/SQL Packages and Types Reference 12c Release 1 (12.1)第153章。
1.2 数据库对象统计信息
除了系统级别的统计信息,oracle还会收集数据库中对象的统计信息。
不同于系统级别的统计信息,oracle默认会自动收集数据库对象的统计信息。
1.2.1 自动收集统计信息
详情参见统计信息自动收集.txt
1.2.1.1部分特例
在11g及以前版本中,创建索引的时候,由于需要进行全表扫描,因此此时Oracle顺便完成了索引的统计信息收集。而创建表时,则没有类似的动作。
在12c中,当用直接路径加载数据,例如create table as select (CTAS)和insert as select(IAS)时,Oracle也顺便搭载了统计信息
的收集工作。这就意味着,在采用这样的方式加载数据之后,不需要额外的全表扫描来收集统计信息了。
1.2.2 手动收集统计信息
1.2.2.1 DBMS_STATS相关过程
oracle的DBMS_STATS包提供了如下收集计信息的过程:
GATHER_DATABASE_STATS; 收集数据库中所有对象的统计信息
GATHER_DICTIONARY_STATS; 收集'SYS','SYSTEM'和RDBMS组件对象的统计信息
GATHER_FIXED_OBJECTS_STATS; 收集所有动态性能表的统计信息
GATHER_INDEX_STATS; 收集索引的统计信息
GATHER_PROCESSING_RATE;
GATHER_SCHEMA_STATS; 收集某一用户的所有对象的统计信息
GATHER_SYSTEM_STATS; 收集系统统计信息
GATHER_TABLE_STATS; 收集表的统计信息
1.2.2.2 统计信息内容
其中,表、列、索引统计信息分别包含如下内容:
表统计信息(user_tables/all_tables/dba_tables):
NUM_ROWS 记录行数
BLOCKS 高水位线以下的数据块数
EMPTY_BLOCKS 高水位线以上数据块数,dbms_stat不统计.
AVG_SPACE 平均空闲空间,不统计
CHAIN_CNT 行迁移行数,不统计
注:本记录实际上会记录发生行迁移或者行链接现象的所有行数,但是dbms_stats中的任意收集统计信息的过程都不处理这些信息,而只是简单的将该值置为0.可以使用
ANALYZE TABLE orders LIST CHAINED ROWS INTO chained_rows;
来获取关于行迁移和行链接的相关信息。其中chained_rows 可以通过
$ORACLE_HOME/rdbms/admin/utlchain.sql脚本来创建。
AVG_ROW_LEN 行平均长度
列统计信息(user_tab_columns):
NUM_DISTINCT 唯一值数量
LOW_VALUE 该列最小值
HIGH_VALUE 该列最大值
DENSITY 表示该列数据的重复率,0到1的小数,越接近0表示重复率越低。
NUM_NULLS 该列null值的数量
AVG_COL_LEN 列平均长度,以字节为单位
HISTOGRAM 是否有直方图统计信息,
none: 没有
frequency: 频率直方图
height balanced 高度直方图
NUM_BUCKETS 直方图桶数
索引统计信息(user_indexes):
BLEVEL 索引的深度,也就是层数
LEAF_BLOCKS 叶子块数目
DISTINCT_KEYS 索引distinct键值数目
AVG_LEAF_BLOCKS_PER_KEY 每一个键值的平均叶子块数目
AVG_DATA_BLOCKS_PER_KEY 每一个键值的平均数据块数目
CLUSTERING_FACTOR 簇聚因子 (反映行与索引键值的有序性)
注:统计信息包括但不局限于以上内容。
1.2.2.3 扩展统计信息
在数据库中,oracle默认同一个表的不同列之间,它们的关系是相互独立的。
也就是说,某一列的取值,不会影响到其他列的取值,也不会受到其他列取值的影响。
而在实际当中,往往会存在取值互相影响或者制约的列之间的关系。
比如我们有一个雇员表,其中一列为出生月份,另外一列为星座。显然这两列之间,是存在一定关系的。
为处理这样的问题,oracle引入了扩展统计信息(extension statistics)的概念。
在oracle中,此处的扩展,指的是列的组合,或者是基于列的表达式。
有如下的例子:
HR@ORCL> alter table hr.employees no flashback archive;
HR@ORCL> select dbms_stats.create_extended_stats('hr','employees','(first_name,last_name,job_id)') from dual;
HR@ORCL> exec dbms_stats.gather_table_stats('hr','employees',method_opt=>'for all columns size 1,for columns (first_name,last_name,job_id) size auto');
HR@ORCL> select * from user_stat_extensions where table_name='EMPLOYEES';
注:扩展统计信息中的列组,与复合索引;
扩展统计信息中的基于列的表达式,与函数索引
它们在影响执行计划的时候有何不同?
另注:自动收集统计信息时,不会收集扩展统计信息。
1.2.2.4 estimate_percent参数分析
GATHER_DATABASE_STATS
GATHER_DICTIONARY_STATS
GATHER_INDEX_STATS
GATHER_SCHEMA_STATS
GATHER_TABLE_STATS
以上五个收集统计信息的过程,都包含了estimate_percent这个参数。
在12.1的官方文档中,oracle对该参数的解释如下:
该参数指定了在收集统计信息时对行采样的百分比(NULL表示全部采样)。其可用值的范围为【0.000001,100】。
可以使用常量DBMS_STATS.AUTO_SAMPLE_SIZE来让oracle自己决定采取适当的数值来收集统计信息。这也是默认行为。
该默认值可以通过
SET_DATABASE_PREFS,
SET_GLOBAL_PREFS,
SET_SCHEMA_PREFS,
SET_TABLE_PREFS进行修改。
注:在设置这些默认项之后,也会作用于统计信息的自动收集。
可以使用dbms_stats.get_prefs获取当前系统与统计信息相关的参数的默认值。
可以获取的参数包括
■ AUTOSTATS_TARGET
■ CASCADE
■ CONCURRENT
■ DEGREE
■ ESTIMATE_PERCENT
■ METHOD_OPT
■ NO_INVALIDATE
■ GRANULARITY
■ PUBLISH
■ INCREMENTAL
■ INCREMENTAL_LEVEL
■ INCREMENTAL_STALENESS
■ STALE_PERCENT
■ GLOBAL_TEMP_TABLE_STATS
■ TABLE_CACHED_BLOCKS
■ OPTIONS
其中,
get_prefs用于获取全局的统计信息收集配置选项。
要获取对象级别的统计信息配置选项:
SELECT * FROM DBA_TAB_STAT_PREFS;
例子如下:
SQL> select dbms_stats.get_prefs('estimate_percent') name from dual;
NAME
--------------------------------------------------------------------------------
DBMS_STATS.AUTO_SAMPLE_SIZE
按照oracle的解释,AUTO_SAMPLE_SIZE为一个常量,其值可以通过如下方式获取:
Connected to Oracle Database 12c Enterprise Edition Release 12.1.0.1.0
Connected as sys@ORCL AS SYSDBA
SQL> create table t_val (name varchar2(30),value number);
Table created
SQL> begin
2 insert into t_val values ('AUTO_SAMPLE_SIZE',DBMS_STATS.AUTO_SAMPLE_SIZE);
3 end;
4 /
PL/SQL procedure successfully completed
SQL> select * from t_val;
NAME VALUE
--------------------------- ----------
AUTO_SAMPLE_SIZE 0
显然这不是我们想要的值。
按照oracle的说明,该值的取值范围,也应该在【0.000001,100】之间。
我们可以设计一个实验,来获取该值的一个近似值。
SQL> create table t as select * from dba_objects;
Table created
SQL> set timing on;
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T',estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE);
PL/SQL procedure successfully completed
Executed in 0.608 seconds
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T',estimate_percent => NULL);
PL/SQL procedure successfully completed
Executed in 0.999 seconds
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T',estimate_percent => 100);
PL/SQL procedure successfully completed
Executed in 0.889 seconds
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T',estimate_percent => 50);
PL/SQL procedure successfully completed
Executed in 0.483 seconds
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T');
PL/SQL procedure successfully completed
Executed in 0.234 seconds
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T',estimate_percent => 60);
PL/SQL procedure successfully completed
Executed in 0.577 seconds
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T',estimate_percent => 62);
PL/SQL procedure successfully completed
Executed in 0.593 seconds
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T',estimate_percent => 65);
PL/SQL procedure successfully completed
Executed in 0.608 seconds
SQL> set timing off;
由上述实验可知,estimate_percent 设置为null时,为全部采样,也就是100。
当将其设置为其默认值DBMS_STATS.AUTO_SAMPLE_SIZE时,收集统计信息消耗的时间与将其设置为65时相同。
鉴于本实验是在12.1的数据库中测试,所以我们基本可以断定:
在12.1中,estimate_percent的默认采样比例,大致在65%。
1.2.2.5 统计信息锁定与force选项
为预防由于统计信息不规则变化影响执行计划,我们可以选择锁定某些对象的统计信息。
oracle提供了如下锁定统计信息的过程:
LOCK_PARTITION_STATS
LOCK_SCHEMA_STATS
LOCK_TABLE_STATS
对应的,也有如下解除锁定的过程:
UNLOCK_PARTITION_STATS
UNLOCK_SCHEMA_STATS
UNLOCK_TABLE_STATS
我们以前面测试estimate_percent中创建的测试表T为例,显示表统计信息的锁定与解锁问题。
SQL> exec dbms_stats.lock_table_stats(ownname => 'SYS',tabname => 'T');
PL/SQL procedure successfully completed
SQL> desc dba_tab_statistics;
Name Type Nullable Default Comments
------------------------- ------------- -------- ------- --------------------------------------------------------------------
OWNER VARCHAR2(128) Y Owner of the object
TABLE_NAME VARCHAR2(128) Y Name of the table
PARTITION_NAME VARCHAR2(128) Y Name of the partition
PARTITION_POSITION NUMBER Y Position of the partition within table
SUBPARTITION_NAME VARCHAR2(128) Y Name of the subpartition
SUBPARTITION_POSITION NUMBER Y Position of the subpartition within partition
OBJECT_TYPE VARCHAR2(12) Y Type of the object (TABLE, PARTITION, SUBPARTITION)
NUM_ROWS NUMBER Y The number of rows in the object
BLOCKS NUMBER Y The number of used blocks in the object
EMPTY_BLOCKS NUMBER Y The number of empty blocks in the object
AVG_SPACE NUMBER Y The average available free space in the object
CHAIN_CNT NUMBER Y The number of chained rows in the object
AVG_ROW_LEN NUMBER Y The average row length, including row overhead
AVG_SPACE_FREELIST_BLOCKS NUMBER Y The average freespace of all blocks on a freelist
NUM_FREELIST_BLOCKS NUMBER Y The number of blocks on the freelist
AVG_CACHED_BLOCKS NUMBER Y Average number of blocks in buffer cache
AVG_CACHE_HIT_RATIO NUMBER Y Average cache hit ratio for the object
SAMPLE_SIZE NUMBER Y The sample size used in analyzing this table
LAST_ANALYZED DATE Y The date of the most recent time this table was analyzed
GLOBAL_STATS VARCHAR2(3) Y Are the statistics calculated without merging underlying partitions?
USER_STATS VARCHAR2(3) Y Were the statistics entered directly by the user?
STATTYPE_LOCKED VARCHAR2(5) Y type of statistics lock
STALE_STATS VARCHAR2(3) Y Whether statistics for the object is stale or not
SCOPE VARCHAR2(7) Y whether statistics for the object is shared or session
SQL> col OWNER for a20
SQL> col TABLE_NAME for a20
SQL> col STATTYPE_LOCKED for a20
SQL> select OWNER,TABLE_NAME,STATTYPE_LOCKED from dba_tab_statistics where TABLE_NAME='T';
OWNER TABLE_NAME STATTYPE_LOCKED
-------------------- -------------------- --------------------
SYS T ALL
在T表的统计信息被锁定之后,是否可以重新搜集该表的统计信息呢?
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T');
begin dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T'); end;
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: 在 "SYS.DBMS_STATS", line 33859
ORA-06512: 在 line 2
显示该统计信息已经被锁定。
我们此处可以用force参数强制收集。
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T',force => true);
PL/SQL procedure successfully completed
强制收集之后,该统计信息依然处于锁定状态:
SQL> select OWNER,TABLE_NAME,STATTYPE_LOCKED from dba_tab_statistics where TABLE_NAME='T';
OWNER TABLE_NAME STATTYPE_LOCKED
-------------------- -------------------- --------------------
SYS T ALL
下次再次收集,依然会报错:
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T');
begin dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T'); end;
ORA-20005: object statistics are locked (stattype = ALL)
ORA-06512: 在 "SYS.DBMS_STATS", line 33859
ORA-06512: 在 line 2
我们可以解锁该统计信息:
SQL> exec dbms_stats.unlock_table_stats(ownname => 'SYS',tabname => 'T');
PL/SQL procedure successfully completed
SQL> select OWNER,TABLE_NAME,STATTYPE_LOCKED from dba_tab_statistics where TABLE_NAME='T';
OWNER TABLE_NAME STATTYPE_LOCKED
-------------------- -------------------- --------------------
SYS T
这样即便不指定force参数,也可以重新收集统计信息了。
SQL> exec dbms_stats.gather_table_stats(ownname => 'SYS',tabname => 'T');
PL/SQL procedure successfully completed
1.2.2.6 用户自定义统计信息表
当我们需要导出存储在系统字典表中的统计信息,
或者打算利用统计信息做一些测试而不影响存储在系统字典表中的统计信息时,
我们就可以创建用户自定义的统计信息表,从而来存储统计信息。
1.2.2.7 12c性能优化白皮书