屯昌旅游网

理解索引

     索引是什么东西呢?索引是如何提高查询的性能的。 对于新手来说, 要弄懂数据库的页, 二叉树等等概念实在是太难了。
难道非得弄清楚这些概念之后才能理解索引吗? 当然不是的, 以下是我对索引的理解,和真实情况并不完全一致, 但是起码
能够帮助我们更直观的理解索引。

简单的说表的索引就相当于 表的一个副表, 不过他只包含该索引所包含的字段, 外加一个指向真实表的地址字段。


下面我们以一个例子来说说索引是如何提高性能的。

1. 非聚集索引

假设有一个学生表: TStu   他包含20 个字段。现在有一个查询

select * from TStu where fName like '%平'

如果没有索引, 数据库怎么查呢?
首先数据库引擎会逐行的读取 TStu 表的每一行记录, 来检查fName 字段是否以‘平’为结尾, 如果是, 则将记录放入结果集。
这个操作叫做 表扫描。

现在我们在fName 字段上建了一个非聚集索引, 按照我先前的说法, 相当于在数据里面建了一个只有2 个字段的附表
一个字段 与 TSTu.fName 一样, 另一个字段是fName对应的行的指针。我们把这个表叫做索引副表好了。

数据库是如何来执行上述查询的呢?

第一步  数据库将扫描索引副表, 找出姓名以 ‘平’ 结尾的学生。
第二部  再根据副表所指示的真实记录的地址去查找改学生得其他字段信息。

由于索引副表只有真实表的 约 10 份之 1大小,第一步查找效率比没有索引时提高了10 倍。
第二步的效率可能出现2 总情况:

如果所有学生的 姓名都是以 ‘平’结尾, 那么数据库引擎最终还是要对真实表的所有记录进行扫描,
这种情况下, 使用索引其实比不使用索引效率更加差。
如果只有很少的学生的姓名是以‘平’结尾,那么数据库引擎只需要对真实表很少的记录进行扫描。
这种情况下, 使用索引比不使用索引效率将高出很多。

我们再来考虑另外一个查询

select fName from TStu where fname like '%平'

这个查询与上一个查询 不同的地方在于 只需要选取fName 字段就好了。

如果不使用索引的话,
数据库引擎还是执行全表扫描,  找出对应的记录,然后取出其中的fName 字段。

如果使用索引的话,
数据库引擎 只需要在 索引副表中找到 fName 字段就好了, 而不需要去检查真实表。 即便是所有学生姓名都是已 ‘平’ 结尾。
查询性能都比原查询快了将近 10 倍。

从上面的分析中可以得出结论。 在记录总数越多, 表的字段越多, 所选出来的记录数越少的情况下, 使用索引比不使用索引性能更加好,

同时索引字段是否包含所需要查询的字段,对查询性能的影响也是明显的。


2. 集索引的优势:
聚集索引其本质就是排了序的索引,并且真实表的物理顺序与索引副表的排序保持一致, 每个表只能有一个物理顺序, 所以每个表就只能有一个聚集索引。
我们考虑下面这个查询:

select * from TStu where fSID  between 12 and 20

这个查询查询学生ID 在12 与 20 之间的考生信息。 使用非聚集索引的情况我们上面已经分析过,
现在我们来分析一下 使用聚集索引的情况:我们假设已经在fSID 字段上建立了聚集索引, 并且按照fSID 的升序排列。

第一步 :数据库引擎在查询的时候首先根据索引统计信息 在索引表中找到fSID 为12 的记录以及fSID 为20 的记录。
第二步 :根据获取的记录以及地址。 再到真实表中找到相应的记录。因为聚集索引是排序顺序就是真实表记录的物理存储顺序。
  所以fSID为12 与 20 之间的记录在物理上是保存在数据库连续的页中。所以只需要做最少的页扫描就能获取所需要的记录。
  非聚集索引由于记录分散在数据库中不同的页中获取每一条记录都必须进行一次页扫描(如果记录总大小超过1页则进行多次页扫描)
  其效率已经大大的提高了。

我们再回过头来看看第一步 的实现:
数据库引擎是如何直接获取 fSID 为12 记录, 而不需要进行扫描操作的呢?数据库将会根据索引统计信息来决定采用那种算法为最优。

在这里我简单的举出可能的 2 种简单算法:

 从上往下扫描法:从索引副表的第一条记录从上往下扫描,直到找到所需要的记录, 就不需要继续往下扫描。 这种情况下查询效率与目标记录的位置有关系
  如果目标记录位于表的最前面, 那么查找将最快。
 折中取值法:   从索引表的中间位置取一条记录进行比较, 如果大于12 则到下半表再取中间值再比较, 如果小于 12 则到上半表取中间值再比较, 以此

类推,直到找到目标记录。

事实上数据库引擎根据索引统计信息以及数据特性往往能以最快的方式找到记录, 而不需要进行耗时的扫描。

由于每个表只能有一个聚集索引, 建立聚集索引的时候一定要慎重, 从以上的分析中可以得出这样结论 :
 1. 在数据中逻辑上存在连续性的字段上建立聚集索引将能够大大的提高性能。 比如日期字段, 按一定规则编码的学号字段。 相反的, 对于逻辑上
 没有必然的连续性的字段则意义相对不大。 比如考生照片字段, 自动增长字段, GUID 字段, 以及其他的一些无规律的编码字段。

 2. 在一个具有适量重复键的字段上建立聚集索引能够大大提高性能。 相反对于一个唯一字段 或者 全部相同字段 建立聚集索引 其效果并不明显。


3. 组合索引:

 理解了单字段索引, 组合索引就很好理解了, 考虑下面一个查询:
 
select * from TSTU where fSID between 100 and 10000 and fName like '%平'

如果单单以fSID 字段建立索引, 则数据库在查找索引副表之后 需要到真实表中扫描9900 条记录。
如果单单以fName 字段建立索引,并且以‘平’ 结尾的记录数特别多, 我们还是需要到真实表中扫描相当多的记录。

如果我们以fSID 与 fName 2 个字段建立组合索引。 经过这2 个字段在索引副表的联合筛选, 我们只需要在真实表中扫描很少的记录就能找到

我们所需要的目标记录, 而真实表的扫描恰恰是最花时间的。

再看下面的查询:

select fSID , fName from TSTU where fSID between 100 and 10000 and fName like '%平'.

这种情况下无论是以fSID 还是以fName  建立索引。 都需要去扫描真实表, 而如果以2 个字段建立联合索引。 则不需要再去扫描真实表。

当然:  组合索引的索引副表随着字段数的增加而增大 必然导致索引副表的扫描时间增加。具体使用要根据实际情况权衡利弊。

从上面的分析我们可以得出结论, 建立组合索引需要考虑以下几个因数:
 
1. 字段数尽可能得少, 这样能减少索引扫描的时间。

2. 对于组合条件的查询, 尽量挑选出能够最大限度限制结果集的大小的字段 作为组合字段索引。

3. 对于只需要查询很少字段的查询, 可以考虑使用组合索引覆盖查询字段。

 

写到这里不知道大家对索引的优化理解了多少,对于文中如果存在不正确的理解请高手指正。 

posted on 2008-12-05 13:19  welkin  阅读(409)  评论(0编辑  收藏  举报

导航

屯昌旅游网