数据库索引——索引优化

一、 名词定义

谓词

简单谓词和复杂谓词
WHERE字句中的每个条件称为一个谓词。
过滤因子
描述了谓词的选择性,即表中满足谓词条件的记录行数所占的比例

过滤因子(FF)= 结果集的数量 / 表行的数量
平均过滤因子 = 1 / 不同列值的数量
索引片及匹配列

一个索引定义一个索引片,如果WHERE字句中有第二个列,而这个列也在索引上,从而使得这两个列能够一同定义一个更窄的索引片,这两个列叫做匹配列
这样不仅显著减少了索引的处理量,更是减少了对标进行同步读的次数。

索引过滤及过滤列

并不是所有的索引列都可参与索引片的定义。
所有的匹配列参与匹配过程——定义索引片。
过滤列参与了索引片的过滤过程,无法参与匹配过程,只能使索引片更厚。
判断谓词能否参与匹配和过滤?

  • 范围谓词后面的列都是非匹配列,如果非匹配列足够简单则为过滤列
  • WHERE子句中从左至右有一个足够简单的谓词与之对应,该列则称为匹配列

二、 三星索引

三星索引是对于一个查询语句可能最好的索引。通过三星索引,一次查询通常只需一次随机读及一次窄索引的扫描。

SELECT CNO, FNAME
FROM    CUST
WHERE  LNAME BETWEEN :LNAME1 AND :LNAME2
	      AND
	      CITY = :CITY
	      ORDER BY FNAME
星级评定
  • 索引中包含WHERE子句中所用到的所有列,查询相关的索引行是相邻的,标记上第一颗星。
    这最小化了必须扫描的索引片的宽度。
    索引过滤可以确保回表访问只发生在所有查询条件都满足的时候。
    构造一星索引满足条件:取出所有等值谓词的列(WHERE COL = …)作为索引最开头的列(任意顺序),我们将会有一个较窄的索引片,后面如果跟上范围谓词,将会使索引片更窄,范围谓词和等值谓词间就不能有其他索引列。
    (CITY, LNAME, FNAME, …)
  • 索引行的顺序与查询语句一致,标记上第二颗星。
    这排除了排序操作。
    构造二星索引满足条件:将ORDER BY的列放入索引中(不改变顺序,除去第一颗星加入的索引列),这些列放在等值谓词前后都可以(因为这些列的索引可以使结果集以其顺序排列,而不需要额外排序),等值谓词值只有一个,也不会影响排序结果;但一定要放在范围谓词之前,范围谓词按顺序扫描后就变成了范围谓词的顺序,ORDER BY后的列就要进行排序操作。
    (CITY, FNAME, LNAME, …)
  • 索引行包含了查询语句中的所有列,标记上第三颗星。
    这样列值都在索引中,不需要访问表,同步读也不会出现问题。
    构造三星索引满足条件:将查询语句中剩余的列加到索引中去,列在索引中添加的顺序对查询语句的性能没有影响,但是将易变的列放在最后能够降低更新的成本。
    (CITY, FNAME, LNAME, CNO)

可以看出,我们的理想索引一定能有第三颗星,但只能有第一颗或者第二颗星。

设计最佳索引

Method A

  1. 取出等值谓词作为起始列,任意顺序
  2. 将选择性最好的范围谓词作为下一个列,只考虑不过分复杂的范围谓词
  3. 以正确顺序添加ORDER BY列,有DESC则加上,忽略1和2步中已经添加的列
  4. 以任意顺序将SELECT语句中其余的列添加至索引中,不易变的列放在前面
    eg.
    (CITY, LNAME, FNAME, CNO)

Method B

  1. 取出等值谓词作为起始列,任意顺序
  2. 以正确顺序添加ORDER BY列,有DESC则加上,忽略1步中已经添加的列
  3. 以任意顺序将SELECT语句中其余的列添加至索引中,不易变的列放在前面
    eg.
    (CITY, FNAME, LNAME, CNO)
  • Method A : 如果一个程序取出结果集的所有行,那么A可能和B一样快,甚至更快。
    增加了排序操作,主要看数据排序时间的占CPU开销多少。数据量大的时候先减少索引片的厚度,再进行排序,后续的操作会在更小的数据量下进行,时间开销自然变小。
  • Method B : 如果一个程序只需获取能够填充满一个屏幕的数据量,比如只取查询语句结果数据的前20条数据,那么B可能会比A快很多。
    访问过程中没有排序的话,数据库管理系统只要一次一次地读取数据行就行了。

三、 合适设计理想索引

并不是所有的查询语句都需要设计理想的最佳索引。
根据上面说的,为每个查询设计最佳索引的过程是机械式的,只要给出以下内容就可以自动完成整个过程:

  1. 查询语句
  2. 数据库统计信息(行数,页数,列值分布等等)
  3. 对于每一个简单谓词或组合谓词最差情况下的过滤因子
  4. 已经存在的索引(只是避免生成重复索引)
多余的索引

我们为查询语句设计了一个最佳索引后,去看一下已经存在的索引是很有必要的,我们不可能会完全按照机械式的方式去创建索引。有可能某一个已经存在的索引几乎和理想索引差不多好用,特别是打算在这个已有索引的最后添加一些列的情况下。
当分析一个已经存在的索引对一个新的查询有多大用处,是否真的需要新建理想索引时,可以从这三种多余索引来看,考虑一种新的折中的索引方式:

  • 完全多余的索引
    一个查询包含 WHERE A = :A AND B = :B,另一个查询包含 WHERE B = :B AND A = :A ,那么生成(A, B) 和 (B, A) 两个索引,如果没有其他查询语句包含A列或B列上的范围谓词的话,那么这两个索引其中一个就是完全多余的。甚至我们能够去质问业务为什么要设计这样的SQL。
  • 近乎多余的索引
    假设已经存在索引(CITY, LNAME, FNAME, CNO),如果有一个新的查询语句包含现在的4个索引列,后面还多跟了10个其他的列,那么在创建了新的索引后,原来的索引应该被删除。
    由于使用14个列的索引,从中取出4个列的数据,CPU时间增长不多,由于顺序读的处理过程主要也是受CPU时间的限制,在性能上不会有很大的影响。
  • 可能多余的索引
    一个新的查询语句的设计理想索引是 (A, B, C, D, E, F),而表上已存在的索引是 (A, B, F, C),一般会在已经存在的索引后加上新增的两列D和E,成为 (A, B, F, C, D, E)。
    理想索引可能有两方面优势:使查询拥有更多匹配列和避免排序。但这两个优势都受需要在索引片上扫描的行数的影响,往往新的索引是不需要的,在现有索引后加上新的列就足够了。
建新索引的代价

如果一个表上有100个不同的查询,且为每一个索引都设计了最佳索引的话,那么即使没有重复索引,该表上最终也可能有非常多的索引。这样一来表的插入,更新和删除操作就会变得很慢。我们一般从三个方面来讨论,建立一个新的索引要付出的代价。

  1. 响应时间
    当我们向表中添加一行数据,它必须在每一个索引上都添加相应的行,在一个索引上添加一行耗时10ms(从磁盘读取一个叶子页),在10个索引上每个索引添加20行就耗时1.8s(主键索引维护在同一个索引页)。
    所以从响应时间的角度来看,在一个拥有许多索引的大表上进行事务操作,可能是无法忍受的。

  2. 磁盘负载
    由于数据库的异步写不会影响到事务响应时间,但是这些写会增加磁盘负载。在缓冲区中被修改过的页是会迟早被写到磁盘上去的,当一张表插入或更新频率较高,缓冲区空闲空间变少或脏页过多,会使得从缓冲区写回磁盘的操作也会变频繁,这时带来的磁盘压力也会变大。而且当索引较多时,向表中插入或更新多行数据也会同时更新多个索引页上的行,磁盘繁忙时间会很久。
    从磁盘负载的角度来看,一个插入频度较低的表,能够容忍在表上建许多索引,索引数量取决于插入事务对响应时间的要求。如果磁盘负载是问题,较明显的解决办法是尝试合并索引。一个有10个列的索引比两个各有6个列的索引所引起的磁盘负载要小。

  3. 磁盘空间
    如果一张表中有1000万行以上的数据,多个索引的磁盘空间成本可能会成为一个问题。即使我们不考虑磁盘空间的成本问题,随着索引变大,缓冲池或磁盘缓存也应随之增大,否则存在磁盘上会使非叶子页的I/O量会增加,所以非叶子页的内存开销也不少。

参考:
https://www.cnblogs.com/chenpingzhao/p/5107480.html
《索引设计与优化》

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值