您的位置:首页 > 数据库 > Oracle

开发人员如何关注Oracle性能(下)

2013-06-24 19:34 316 查看

4
Oracle索引监控:

启动/关闭索引监控:

在某个典型业务周期开始之前,执行如下命令对需要关注的索引启动监控功能:

ALTER INDEX INDEX_NAME monitoring usage;

在某个典型业务周期结束之后,执行如下命令结束监控功能:

ALTER INDEX INDEX_NAME nomonitoring usage;

5
索引扫描类型:

5.1
INDEX UNIQUE SCAN

通过精确匹配主键(或者存在UNIQUE约束的列)查询结果返回一行,则称为索引唯一扫描。例如:

SELECT * FROM EMP WHERE EMPNO=7499;

5.2
INDEX RANGE SCAN:

SQL语句查询结果返回多行,称为索引范围扫描。例如:

SELECT * FROM EMP WHERE DEPTNO=20;(前提是给表EMP的列DEPTNO创建了索引)

5.3
INDEX FULL SCAN:

当CBO根据统计数值得知进行全Oracle索引扫描比进行全表扫描更有效时,才进行全Oracle索引扫描,

5.4
INDEX FAST FULL SCAN:

与INDEX FULL SCAN类似,但是一个显著的区别就是它不对查询出的数据进行排序,即数据不是以排序形式被返回。可以使用/*+INDEX_FFS(TABLE_NAME INDEX_NAME)*/实现。或者,在查询总记录数接近表总记录数时也会采用INDEX
FAST FULL SCAN。例如某个表TEST中含有 100 000条数据,当SQL语句(SELECT * FROM
TEST WHERE COL2=22,在表TEST的字段COL2上创建了索引)查询的结果有接近100 000条,那么此时Oracle会采用全Oracle索引快速扫描。

6
进行索引碎片分析和整理:

频繁对索引字段进行DELETE、UPDATE操作,会对索引造成大量碎片,从而极大的影响索引的使用效率,并造成索引I/O增加;

6.1
索引碎片分析:

执行如下语句可以检测索引的碎片情况:

analyze index index_name validate structure online;

select name,del_lf_rows_len,lf_rows_len,(del_lf_rows_len/lf_rows_len)*100 PERCENT from index_stats;

其中(del_lf_rows_len/lf_rows_len)*100表示索引碎片率(%)。

6.2
索引碎片整理:

Oracle进行索引碎片整理包括两种策略:

(1)
重建索引:ALTER INDEX INDEX_NAME REBUILD [NOLOGGING];

(2)
压缩索引:ALTER INDEX INDEX_NAME COALESCE;

建议采取定期索引重建(Rebuild)的策略,例如,可在每个周末或者每天晚上对删除操作频繁表的索引进行在线重建工作。

当我们使用CBO的方式,就应当及时去更新表和索引的统计信息,以免生成不切合实的执行计划:
ANALYZE TABLE table_name COMPUTE STATISTICS;
ANALYZE INDEX index_name ESTIMATE STATISTICS;

7
SQL语句执行过程:

7.1
Parse阶段:

Oracle将首先在SHARE POOL中搜索该语句,即判断该语句是否已经被分析和执行过,如果没有发现该语句,则需要检查该语句的语法、检查该语句的语义以及访问权限、对视图定义和子查询语句进行转换操作,并最终确定该语句优化的执行计划,这个完成的分析过程叫Hard
Parse,如果在SHARE POOL中发现了改语句,说明该语句已经被分析和执行过,则只需要检查该语句的语义以及访问权限,而其他大部分工作则无需再进行了,这个过程叫Soft Parse,Hard Parse比Soft
Parse的资源消耗大多了。

7.2
Bind阶段:

当SQL语句含有BIND变量时,Oracle通过赋值或者传参等方式为这些BIND变量赋值。

7.3
Execute阶段:

Oracle实施Parse阶段确定的执行计划,开始DML语句,实施I/O以及排序操作等。如果是DDL,DML操作,完成此阶段执行过程结束。

7.4
Fetch阶段:

只适合于SELECT操作,Oracle建议以数组的方式成批提取记录,降低服务器和客户端网络传输次数(Round-Trip)。

7.5
如何BIND变量:

例如根据员工编号查询员工信息:SELECT * FROM EMP WHERE EMPNO=某个值;

并发量访问非常大的情况下,这条SQL语句应该修改成:

SELECT * FROM EMP WHERE EMPNO=:EMP_NO;

其中EMP_NO是个变量,在真正执行过程中,分别为X赋值1234,2345,4567……

如何实现语句共享化:

LOOP

CURSOR CUR;

NUMBER EMP_NO:=程序调用该存储过程传进来的值;

PARSE(CUR, "SELECT * FROM EMP WHERE EMPNO=:VALUE ");

BIND(CUR, ":VALUE ",EMP_NO) ;

EXECUTE(CUR);

FETCH(CUR);

CLOSE(CUR);

END LOOP ;

Execute to Parse%=(Execute次数-Parse次数)/Execute次数;该指标值越低,说明Parse次数越高,系统整体语句共享性越差,一个运行良好的系统该指标值应该达到70%以上。

系统级解决方案:ALTER SYSTEM SET CURSOR_SHARING=’SIMILAR’;(默认EXACT,可选值EXACT,SIMILAR,FORCE),建议采用应用级绑定化处理。

SQL语句的子查询在大部分情况下,原理上等同于多表连接操作。因此Oracle优化器在Parse阶段会尽可能将子查询转化为多表连接操作。因此,开发人员书写子查询语句时多此一举。而且子查询的可读性很差,子查询的方式可能会强制Oracle优化器选择错误的执行路径,导致整个语句的性能急剧下降。

7.6
IN OR EXISTS:

7.6.1
IN:

IN的原理是先进行子查询操作,再进行主查询操作。

SELECT E.* FROM EMP E WHERE E.DEPTNO IN

(SELECT D.DEPTNO FROM DEPT D WHERE D.DNAME=’SALES’);

执行过程:

如果在DNAME上创建了索引,那么先按DNAME上的索引访问DEPT表,再按建立在EMP表的DEPTNO字段上的索引访问EMP表。此时,EMP表成了被驱动表。

7.6.2
EXISTS:

EXISTS原理是先进行主查询操作,再到子查询中进行过滤:

SELECT E.* FROM EMP E WHERE EXISTS

(SELECT 1 FROM DEPT D WHERE E.DEPTNO=D.DEPTNO AND D.DNAME=’SALES’);

执行过程:

先进行EMP表的全表扫描,再根据每个员工的部门号DEPTNO去DEPTNO中查询是否是SALES部门进行过滤。

多表查询时,尽量将限制性最强的表作为驱动表。能不用子查询尽量不用子查询,充分相信Oracle优化器能自动贯彻“尽量将限制性最强的表作为驱动表”的原则。

建立复合的Global Partition索引:

CREATE INDEX IDX__MULTI_GLOBAL_PART ON TABLE_NAME(COL_NAME1,COL_NAME2)

GLOBAL PARTITION BY RANGE(COL_NAME1)

( partition p1 values less than('1440010000'),

partition p2 values less than('1440020000'),

partition p3 values less than('1440030000'),

partition p4 values less than('1440040000'),

partition p5 values less than('MAXVALUE'))

TABLESPACE CTAIS2_INDEX NOLOGGING PARALLEL 4;

NOLOGGING表示创建索引时不记录日志,这样能够减少创建索引时间。

PARALLEL表示并行度,建议其值等于CPU核心数,单核不建议用并行,反而会增加创建时间。

背景:字段COL_NAME1的值的跨度比较大,同时又需要给该字段创建索引,那么此时采用Oracle的复合Global Partition索引。

附:Linux命令行中执行SQL语句:

The_Total_Num=`su - ${ORACLEUSER} -c "sqlplus -s / as sysdba <<EOF

conn $DBUSERNAME/$DBPASSWORD;

SELECT COUNT(*) FROM ${THE_TABLE_NAME};

exit

EOF

" | grep -v "^${FBS_BLANKS}$" | tail -1 | tr -d [' '] | tr -d ['\t']`;

8
Oracle全文检索技术:

全文检索是为了解决系统提供模糊和匹配查询功能性能的。

例如,名字中含有A的员工信息:SELECT * FROM EMP WHERE ENAME LIKE '%A%';

针对该SQL语句,即使在ENAME字段上建立了索引,语句的执行计划任然会是全表扫描。

Oracle中满足模糊匹配查询功能的技术就是:采用Oracle Text的全文检索技术。

在ENAME字段上创建Oracle Text的context索引:

CREATE INDEX IDX_CONTEXT_ENAME ON EMP(ENAME) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS('LEXER MYLEXER STORAGE MYSTORE FILTER CTXSYS.NULL_FILTER');

所用全文检索后,SQL语句需要重写:

SELECT * FROM EMP WHERE CONTAINS(EMP, 'A',1)>0;

CREATE INDEX IDX_CONTEXT_DNAME ON DEPT(DNAME) INDEXTYPE IS CTXSYS.CONTEXT PARAMETERS('LEXER MYLEXER STORAGE MYSTORE FILTER CTXSYS.NULL_FILTER');

所用全文检索后,SQL语句需要重写:

SELECT * FROM DEPT WHERE CONTAINS(DNAME, 'A',1)>0;

Oracle Text技术不仅支持普通字符类型字段,还支持CLOB、BLOB等字段。

9
Hint:

注意:在使用Hint技术时,表有别名,Hint语句中一定要使用表的别名,否则Hint会失效。,对于所有的Hint,当访问索引导致结果不完整时,优化器忽略该Hint。

FULL Hint:/*+FULL(TABLE_NAME)*/告诉优化器对表进行全表扫描方式访问数据库。

INDEX Hint:/*+INDEX(TABLE_NAME INDEX_NAME)*/告诉优化器对表通过索引的方式访问数据。

NO_INDEX Hint:/*+NO_INDEX(TABLE_NAME INDEX_NAME)*/告诉优化器对表查询时不要使用索引。

INDEX_DESC Hint:/*+INDEX_DESC(TABLE_NAME INDEX_NAME)*/告诉优化器对指定的索引使用降序方式访问数据库。

INDEX_COMBINE Hint:/*+INDEX_COMBINE(TABLE_NAME INDEX_NAME)*/告诉优化器强制使用位图索引查询。

INDEX_FFS Hint:/*+INDEX_FFS(TABLE_NAME INDEX_NAME)*/告诉优化器强制使用INDEX FAST FULL SCAN方式访问数据库查询。

INDEX_SS Hint:/*+INDEX_SS(TABLE_NAME INDEX_NAME)*/告诉优化器强制使用INDEX SKIP SCAN的方式访问数据库查询。Oracle
9i以后才有这种索引访问方式。当在一个复合索引中,某些SQL语句的条件中并不包含复合索引的第一列时,可以通过INDEX SKIP SCAN来访问索引获得数据。

10
分区技术:

分区前后处理上的差异:

1.
DELETE

如果一张表按照年份进行分区,如果没有分区,势必要通过传统的DELETE操作实现,这种方式效率低下,而且会在原有的表上留下大量碎片,同时产生大量日志文件。

如果使用了分区技术,则可以使用一条简单的SQL语句:ALTER TABLE TABLE_NAME TRUNCATE 2013_PARTITION_TABLE;命令,几乎瞬间清除,而且没有碎片,产生的日志文件也非常少。

2.
SELECT

假设用户要查询2013年的所有数据,如果没有分区,则会在整个达标进行全表扫描。而如果按照年份进行分区,Oracle会只能使用分区裁减(partition pruning)功能,只扫描2005年分区即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: