Oracle 索引的三个问题

五马第一人

五马第一人

2016-02-19 17:23

今天图老师小编给大家介绍下Oracle 索引的三个问题,平时喜欢Oracle 索引的三个问题的朋友赶紧收藏起来吧!记得点赞哦~

  索引( Index )是常见的数据库对象,它的设置好坏、使用是否得当,极大地影响数据库应用程序和Database 的性能。虽然有许多资料讲索引的用法, DBA 和 Developer 们也经常与它打交道,但笔者发现,还是有不少的人对它存在误解,因此针对使用中的常见问题,讲三个问题。此文所有示例所用的数据库是 Oracle 8.1.7 OPS on HP N series ,示例全部是真实数据,读者不需要注意具体的数据大小,而应注意在使用不同的方法后,数据的比较。本文所讲基本都是陈词滥调,但是笔者试图通过实际的例子,来真正让您明白事情的关键。

  第一讲、索引并非总是最佳选择

  如果发现Oracle 在有索引的情况下,没有使用索引,这并不是Oracle 的优化器出错。在有些情况下,Oracle 确实会选择全表扫描(Full Table Scan),而非索引扫描(Index Scan)。这些情况通常有:

  1. 表未做statistics, 或者 statistics 陈旧,导致 Oracle 判断失误。

  2. 根据该表拥有的记录数和数据块数,实际上全表扫描要比索引扫描更快。

  对第1种情况,最常见的例子,是以下这句sql 语句:

  

select count(*) from mytable;

  在未作statistics 之前,它使用全表扫描,需要读取6000多个数据块(一个数据块是8k), 做了statistics 之后,使用的是 INDEX (FAST FULL SCAN) ,只需要读取450个数据块。但是,statistics 做得不好,也会导致Oracle 不使用索引。

  第2种情况就要复杂得多。一般概念上都认为索引比表快,比较难以理解什么情况下全表扫描要比索引扫描快。为了讲清楚这个问题,这里先介绍一下Oracle 在评估使用索引的代价(cost)时两个重要的数据:CF(Clustering factor) 和 FF(Filtering factor).

  CF: 所谓 CF, 通俗地讲,就是每读入一个索引块,要对应读入多少个数据块。

  FF: 所谓 FF, 就是该sql 语句所选择的结果集,占总的数据量的百分比。

  大约的计算公式是:FF * (CF + 索引块个数) ,由此估计出,一个查询, 如果使用某个索引,会需要读入的数据块块数。需要读入的数据块越多,则 cost 越大,Oracle 也就越可能不选择使用 index. (全表扫描需要读入的数据块数等于该表的实际数据块数)

  其核心就是, CF 可能会比实际的数据块数量大。CF 受到索引中数据的排列方式影响,通常在索引刚建立时,索引中的记录与表中的记录有良好的对应关系,CF 都很小;在表经过大量的插入、修改后,这种对应关系越来越乱,CF 也越来越大。此时需要 DBA 重新建立或者组织该索引。

  如果某个sql 语句以前一直使用某索引,较长时间后不再使用,一种可能就是 CF 已经变得太大,需要重新整理该索引了。

  FF 则是Oracle 根据 statistics 所做的估计。比如, mytables 表有32万行,其主键myid的最小值是1,最大值是409654,考虑以下sql 语句:

  

Select * from mytables where myid=1; 和Select * from mytables where myid=400000

  这两句看似差不多的 sql 语句,对Oracle 而言,却有巨大的差别。因为前者的 FF 是100%, 而后者的 FF 可能只有 1%。如果它的CF 大于实际的数据块数,则Oracle 可能会选择完全不同的优化方式。而实际上,在我们的数据库上的测试验证了我们的预测. 以下是在HP 上执行时它们的 explain plan:

  第一句:

  

SQL select * from mytables where myid=1; 

  已选择325917行。

  

Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3132 Card=318474 Byt es=141402456)1 0 TABLE ACCESS (FULL) OF 'MYTABLES' (Cost=3132 Card=318474 Byt es=141402456)Statistics----------------------------------------------------------7 recursive calls89 db block gets41473 consistent gets19828 physical reads0 redo size131489563 bytes sent via SQL*Net to client1760245 bytes received via SQL*Net from client21729 SQL*Net roundtrips to/from client1 sorts (memory)0 sorts (disk)325917 rows processed

  第二句:

  

Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=346 Card=663 Bytes=2 94372)1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MYTABLES' (Cost=346 Card=663Bytes=294372)2 1 INDEX (RANGE SCAN) OF 'PK_MYTABLES' (UNIQUE) (Cost=5 Card=663)Statistics----------------------------------------------------------1278 recursive calls0 db block gets6647 consistent gets292 physical reads0 redo size3544898 bytes sent via SQL*Net to client42640 bytes received via SQL*Net from client524 SQL*Net roundtrips to/from client1 sorts (memory)0 sorts (disk)7838 rows processed 

  显而易见,第1句没有使用索引,第2句使用了主键索引pk_mytables. FF的巨大影响由此可见一斑。由此想到,我们在写sql 语句时,如果预先估计一下 FF, 你就几乎可以预见到 Oracle 会否使用索引。

  第二讲、索引也有好坏

  索引有 B tree 索引, Bitmap 索引, Reverse b tree 索引, 等。最常用的是 B tree 索引。 B 的全称是Balanced , 其意义是,从 tree 的 root 到任何一个leaf ,要经过同样多的 level. 索引可以只有一个字段(Single column), 也可以有多个字段(Composite),最多32个字段,8I 还支持 Function-based index. 许多developer 都倾向于使用单列B 树索引。

  所谓索引的好坏是指:

  1,索引不是越多越好。特别是大量从来或者几乎不用的索引,对系统只有损害。OLTP系统每表超过5个索引即会降低性能,而且在一个sql 中, Oracle 从不能使用超过 5个索引。

  2,很多时候,单列索引不如复合索引有效率。

  3,用于多表连结的字段,加上索引会很有作用。

  那么,在什么情况下单列索引不如复合索引有效率呢?有一种情况是显而易见的,那就是,当sql 语句所查询的列,全部都出现在复合索引中时,此时由于 Oracle 只需要查询索引块即可获得所有数据,当然比使用多个单列索引要快得多。(此时,这种优化方式被称为 Index only access path)

  除此之外呢?我们还是来看一个例子吧:

  在 HP(Oracle 8.1.7) 上执行以下语句:

  

select count(1) from mytabs where coid=130000 and issuedate = to_date ('2001-07-20', 'yyyy-mm-dd')。

  一开始,我们有两个单列索引:I_mytabs1(coid), I_mytabs2(issuedate), 下面是执行情况:

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

  

COUNT(1)----------6427Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=384 Card=1 Bytes=11)1 0 SORT (AGGREGATE)2 1 TABLE ACCESS (BY INDEX ROWID) OF 'T_MYTABS' (Cost=384 Card=126 Bytes=1386)3 2 INDEX (RANGE SCAN) OF 'I_MYTABS2' (NON-UNIQUE) (Cost=11Card=126)Statistics----------------------------------------------------------172 recursive calls1 db block gets5054 consistent gets2206 physical reads0 redo size293 bytes sent via SQL*Net to client359 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client5 sorts (memory)0 sorts (disk)1 rows processed 

  可以看到,它读取了7000个数据块来获得所查询的 6000多行。

  现在,去掉这两个单列索引,增加一个复合索引I_mytabs_test ( coid, issuedate), 重新执行,结果如下:

  

COUNT(1)----------6436Execution Plan----------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=1 Bytes=11)1 0 SORT (AGGREGATE)2 1 INDEX (RANGE SCAN) OF 'I_MYTABS_TEST' (NON-UNIQUE) (Cost=3 Card=126 Bytes=1386)Statistics----------------------------------------------------------806 recursive calls5 db block gets283 consistent gets76 physical reads0 redo size293 bytes sent via SQL*Net to client359 bytes received via SQL*Net from client2 SQL*Net roundtrips to/from client3 sorts (memory)0 sorts (disk)1 rows processed 

  可以看到,这次只读取了300个数据块。

  7000块对300块,这就是在这个例子中,单列索引与复合索引的代价之比。这个例子提示我们, 在许多情况下,单列索引不如复合索引有效率。

  可以说,在索引的设置问题上,其实有许多工作可以做。正确地设置索引,需要对应用进行总体的分析。

  第三讲、索引再好,不用也是白搭

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

  抛开前面所说的,假设你设置了一个非常好的索引,任何傻瓜都知道应该使用它,但是Oracle 却偏偏不用,那么,需要做的第一件事情,是审视你的 sql 语句。

  Oracle 要使用一个索引,有一些最基本的条件:

  1, where 子句中的这个字段,必须是复合索引的第一个字段;

  2, where 子句中的这个字段,不应该参与任何形式的计算

  具体来讲,假设一个索引是按 f1, f2, f3的次序建立的,现在有一个 sql 语句, where 子句是 f2 = : var2, 则因为 f2 不是索引的第1个字段,无法使用该索引。

  第2个问题,则在我们之中非常严重。以下是从 实际系统上面抓到的几个例子:

  

Select jobid from mytabs where isReq='0' and to_date (updatedate) = to_Date ( '2001-7-18', 'YYYY-MM-DD');

  以上的例子能很容易地进行改进。请注意这样的语句每天都在我们的系统中运行,消耗我们有限的cpu 和 内存资源。

  除了1,2这两个我们必须牢记于心的原则外,还应尽量熟悉各种操作符对 Oracle 是否使用索引的影响。这里我只讲哪些操作或者操作符会显式(explicitly)地阻止 Oracle 使用索引。以下是一些基本规则:

  1, 如果 f1 和 f2 是同一个表的两个字段,则 f1f2, f1=f2, f1

  2, f1 is null, f1 is not null, f1 not in, f1 !=, f1 like ‘%pattern%’;

  3, Not exist

  4, 某些情况下,f1 in 也会不用索引;

  对于这些操作,别无办法,只有尽量避免。比如,如果发现你的 sql 中的 in 操作没有使用索引,也许可以将 in 操作改成 比较操作 + union all。笔者在实践中发现很多时候这很有效。

  但是,Oracle 是否真正使用索引,使用索引是否真正有效,还是必须进行实地的测验。合理的做法是,对所写的复杂的 sql, 在将它写入应用程序之前,先在产品数据库上做一次explain . explain 会获得Oracle 对该 sql 的解析(plan),可以明确地看到 Oracle 是如何优化该 sql 的。

  如果经常做 explain, 就会发现,喜爱写复杂的 sql 并不是个好习惯,因为过分复杂的sql 其解析计划往往不尽如人意。事实上,将复杂的 sql 拆开,有时候会极大地提高效率,因为能获得很好的优化。当然这已经是题外话了。

展开更多 50%)
分享

猜你喜欢

Oracle 索引的三个问题

编程语言 网络编程
Oracle 索引的三个问题

Oracle Index 的三个问题

编程语言 网络编程
Oracle Index 的三个问题

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

PPT图表设计的三个问题

电脑入门
PPT图表设计的三个问题

拍照前要想的三个问题

摄影 人像摄影 静物摄影
拍照前要想的三个问题

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

冬季受孕要考虑的三个问题

怀孕
冬季受孕要考虑的三个问题

Oracle出现六个安全漏洞 三个高危

编程语言 网络编程
Oracle出现六个安全漏洞 三个高危

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

解读Absolute与Relative

解读Absolute与Relative

文字究竟底能多大?分析网页文字字号

文字究竟底能多大?分析网页文字字号
下拉加载更多内容 ↓