记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中.使用索引,在一般情况下,将能明显提高查询的性能,但系统为维护索引,也必将增加许多额外的开销。
所以,何时应建立索引,查询时是否使用索引,对系统性能的影响将是非常大的。
在这里,我想对这个问题谈一下自己的认识。
首先,在下列情况下,不适合建立索引:1、表的规模不大,在这种情况下,直接查找表的开销比搜索索引再定位的开销要小。
2、表被频繁更新,在这种情况下,维护索引的开销要大于使用索引所带来的性能提高。
3、表上已经建立了许多索引。
4、用户的查询方式经常发生变化。
上述这些情况都是比较直观的,但是,即使建立了索引,在具体查询时,系统也未必会使用该索引。
不管是何种数据库系统,其查询优化过程由两个层次构成:代数优化(或称基于规则的优化)和物理优化(或称基于代价的优化)(部分数据库系统可能不含物理优化过程)。
代数优化是使用一组预定义的规则来对查询进行优化,在这种优化方式下,如果表上建有索引,系统将使用该索引。
物理优化是在代数优化的基础上,根据物理统计信息,来估计各种执行方案的执行代价,从中选取一种最优(代价最小)的执行方案。
在这种优化方式下,如果表上建有索引,是否使用索引,将取决于查询的“选中度”(selectivity)。
什么是选中度?举个例子,假设表中有一名为“年龄”的字段,有一查询需要查出该表中所有“年龄”不超过50岁的记录,如果表中有70%的记录满足这一条件,则称该查询的选中度为70。
当选中度超过某一预先给定的值P(P的大小取决于系统的具体实现)时,遍历整个表的开销比搜索索引再定位的开销要小,此时系统将不使用索引。
通过统计字段的值分布,可以估计查询的选中度,如果它大于P,系统将不使用索引,直接遍历表。
这是一种非常重要的统计信息,它还可用于估计连接操作结果集的大小。
当然,当查询比较固定时,用户也可以根据自己对应用的理解预先估计选中度,如果太大,则不应建立索引。
不过,最理想的方式是,系统能根据查询的特点和统计信息,自主选择是否建立和使用索引,即索引对用户应是透明的。
目前关于这方面的研究也正在进行之中,比较成功的原型系统是Microsoft公司的Phoenix系统(部分技术已用于SQL-SERVER的最新版本中),如果需要了解这一系统更详细的信息,可以访问/db/phoenix 最后,我再来谈谈索引的种类,B+树和HASH表是最常用的两种索引,前者适用于大型的表,后者则适用于较小的表。
ORACLE8i实现了一种所谓的“位图”索引,这种索引比较适合于字段的取值范围较小,且分布比较平均的表。
例如,假设表中有一布尔类型的字段,它的取值包括“真”、“假”、“未知”三种,我们可以为这三个值分别建立一个大小相同的位图,位图中的一个位与表中的一条记录一一对应。
以“真”值位图为例,假设某记录的该字段取值为“真”,则它在位图中的对应位为“1”,否则为“0”。
这样,假设现在需要查询该表中所有该字段取值为“真”的记录,则只要根据该位图,找到所有为“1”的位,将它们对应的记录取出即可。
在专用数据库中,往往也使用其它类型的索引(如R树)。
创建索引不是为了在sql语句中用的,而是可以大大提高系统的性能。
第一,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
第二,可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
第三,可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
第四,在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
第五,通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
1.合理使用索引索引是数据库中重要的数据结构,它的根本目的就是为了提高查询效率。
现在大多数的数据库产品都采用IBM最先提出的ISAM索引结构。
索引的使用要恰到好处,其使用原则如下:●在经常进行连接,但是没有指定为外键的列上建立索引,而不经常连接的字段则由优化器自动生成索引。
●在频繁进行排序或分组(即进行group by或order by操作)的列上建立索引。
●在条件表达式中经常用到的不同值较多的列上建立检索,在不同值少的列上不要建立索引。
比如在雇员表的“性别”列上只有“男”与“女”两个不同值,因此就无必要建立索引。
如果建立索引不但不会提高查询效率,反而会严重降低更新速度。
●如果待排序的列有多个,可以在这些列上建立复合索引(compound index)。
●使用系统工具。
如Informix数据库有一个tbcheck工具,可以在可疑的索引上进行检查。
在一些数据库服务器上,索引可能失效或者因为频繁操作而使得读取效率降低,如果一个使用索引的查询不明不白地慢下来,可以试着用tbcheck工具检查索引的完整性,必要时进行修复。
另外,当数据库表更新大量数据后,删除并重建索引可以提高查询速度。
2.避免或简化排序应当简化或避免对大型表进行重复的排序。
当能够利用索引自动以适当的次序产生输出时,优化器就避免了排序的步骤。
以下是一些影响因素:●索引中不包括一个或几个待排序的列;●group by或order by子句中列的次序与索引的次序不一样;●排序的列来自不同的表。
为了避免不必要的排序,就要正确地增建索引,合理地合并数据库表(尽管有时可能影响表的规范化,但相对于效率的提高是值得的)。
如果排序不可避免,那么应当试图简化它,如缩小排序的列的范围等。
3.消除对大型表行数据的顺序存取在嵌套查询中,对表的顺序存取对查询效率可能产生致命的影响。
比如采用顺序存取策略,一个嵌套3层的查询,如果每层都查询1000行,那么这个查询就要查询10亿行数据。
避免这种情况的主要方法就是对连接的列进行索引。
例如,两个表:学生表(学号、姓名、年龄……)和选课表(学号、课程号、成绩)。
如果两个表要做连接,就要在“学号”这个连接字段上建立索引。
还可以使用并集来避免顺序存取。
尽管在所有的检查列上都有索引,但某些形式的where子句强迫优化器使用顺序存取。
下面的查询将强迫对orders 表执行顺序操作:SELECT *FROM orders WHERE (customer_num=104 AND or der_num> 1001) OR order_num=1008虽然在customer_num和order_num上建有索引,但是在上面的语句中优化器还是使用顺序存取路径扫描整个表。
因为这个语句要检索的是分离的行的集合,所以应该改为如下语句:SELECT *FROM orders WHERE customer_num=104 AND ord er_num> 1001UNIONSELECT *FROM orders WHERE order_num=1008这样就能利用索引路径处理查询。
4.避免相关子查询一个列的标签同时在主查询和where子句中的查询中出现,那么很可能当主查询中的列值改变之后,子查询必须重新查询一次。
查询嵌套层次越多,效率越低,因此应当尽量避免子查询。
如果子查询不可避免,那么要在子查询中过滤掉尽可能多的行。
5.避免困难的正规表达式MATCHES和LIKE关键字支持通配符匹配,技术上叫正规表达式。
但这种匹配特别耗费时间。
例如:SELECT *FROM customer WHERE zipcode LIKE “98_ _ _”即使在zipcode字段上建立了索引,在这种情况下也还是采用顺序扫描的方式。
如果把语句改为SELECT *FROM customer WHERE zipcode > “98000”,在执行查询时就会利用索引来查询,显然会大大提高速度。
另外,还要避免非开始的子串。
例如语句:SELECT *FROM customer WHERE zipcode[2,3] > “80”,在where子句中采用了非开始子串,因而这个语句也不会使用索引。
6.使用临时表加速查询把表的一个子集进行排序并创建临时表,有时能加速查询。
它有助于避免多重排序操作,而且在其他方面还能简化优化器的工作。
例如:SELECT ,rcvbles.balance,……other columnsFROM cust,rcvblesWHERE cust.customer_id = rcvlbes.customer_idAND rcvblls.balance> 0AND cust.postcode> “98000”ORDER BY 如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个临时文件中,并按客户的名字进行排序:SELECT ,rcvbles.balance,……other columnsFROM cust,rcvblesWHERE cust.customer_id = rcvlbes.customer_idAND rcvblls.balance> 0ORDER BY INTO TEMP cust_with_balance然后以下面的方式在临时表中查询:SELECT *FROM cust_with_balanceWHERE postcode> “98000”临时表中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。
注意:临时表创建后不会反映主表的修改。
在主表中数据频繁修改的情况下,注意不要丢失数据。
7.用排序来取代非顺序存取非顺序磁盘存取是最慢的操作,表现在磁盘存取臂的来回移动。
SQL语句隐藏了这一情况,使得我们在写应用程序时很容易写出要求存取大量非顺序页的查询。
有些时候,用数据库的排序能力来替代非顺序的存取能改进查询。