索引优化与设计(4)-为SELECT语句创建理想的索引

本章节主要覆盖如下内容:

  • 影响表以及索引扫描性能的主要因素列表
  • 随机/顺序读时间以及CPU成本
  • 根据三个最重要的需求为查询语句的索引指定星级
  • 三星索引的设计-最理想的索引设计
  • 宽索引
  • 为查询语句设计最好索引的算法
  • 根据现存索引情况设计最使用的索引,将CPU时间、磁盘读时间和耗费时间(elapsed time)考虑在内
  • 从维护开销来看,对现有索引进行所建议的改变可能产生的结果
  • 响应时间、驱动负载和磁盘代价
  • 一些建议

简介

在SQL查询中,当程序中的SQL使用了一个或者多个索引的时候,许多的DBA就会对此表示满意,但是,使用一个不合适的索引有可能会导致比全表扫描更差的性能。

接下来,本章就会重点分析这其中的缘由,首先,给出我们接下来分析所依赖的前提。

磁盘以及CPU时间的基础假设

下图是给出的磁盘和CPU的基础假设。
disk and cpu time

评注:由于作者写作的时间是2005年,所以这些数据有的已经发生变化,磁盘顺序读可以达到100M/S。具体请参考磁盘性能指标–IOPS、吞吐量及测试[1]。关于CPU time的定义,请参考此文章[2]

不合适索引(inadequate index)

此处给出了一个例子,就是说明,使用索引并不一定比全表扫描好。
假设有如下的一个查询语句,仅有两个合理的访问路径:
1、使用索引(LNAME)
2、全表扫描
SQL 4.1

索引列是LNAME 和 FNAME,并且过滤因子是1%。在这个查询语句中,显然只能够使用索引列LNAME。

对于第一种情况来说,DBMS会选择谓词条件LNAME=:LNAME扫描索引片。对于索引片中的每一个索引行,DBMS都必须回到表中校验CITY字段的值。由于表中的行是根据CNO字段而不是LNAME字段来聚簇的,所以这个校验操作需要做一次磁盘随机读[评注:即先读取了索引数据,然后再去表中进行随机读]。

假设索引(LNAME,FNAME)的总大小是1000,000×100byte = 100M。包括数据以及分散的空闲时间,另外,在假设顺序读是40MB/s[评注:现在可以达到100MB/s].那么读取宽度为1%的索引片,即1MB,需要花费10ms+1MB/40MB/s=35ms。这显然没有问题,但是10,000次[评注:因为索引总条数为1000,000条,过滤因子为1%,所以结果条数为10,000.相应的,去表中随机读的次数就为10,000次]随机读将花费10,000*10ms=100s。所以,总的花费时间为100s+35ms。这种方式太慢了。

对于第二种方式来说,只需要第一个页是随机读。如果表的大小为1000,000×600byte=600MB,包括分散的空闲时间,那么花费的IO时间将会是10ms+600MB/40MB/s=15s,虽然仍然比较慢,但是相比使用索引已经变得快多了。

第二种方案的CPU时间将会比第一种方案的CPU时间长很多,因为DBMS必须对1000,000行而不是20,000行[评注:索引10,000行;数据表10,000行],并且还需要对这些行进行排序[评注:因为SQL语句有ORDER BY]。从另一个角度来说,由于是顺序读,CPU时间可以与IO时间交叠。在这个场景下,全表扫描比在不合适的索引上扫描要快很多,但是这还不够快,仍然需要一个更好的索引。

三星索引(three-star index)-查询语句的理想索引

前面讨论了一个非常不合适的索引,这一小节,我们来讨论

#

参考文献

[1] http://wushank.blog.51cto.com/3489095/1708168
[2] https://www.techopedia.com/definition/2858/cpu-time