想要设计出好的索引,首先必须了解SQL语句在数据库服务器中的处理过程,本文介绍数据库索引设计与优化中几个对索引优化很是重要的概念。mysql
谓词就是条件表达式。 SQL语句的where子句由一个或者多个谓词组成。sql
WHERE SEX = 'M'
AND
(WHIGHT > 90
OR
HEIGHT > 190)
复制代码
上面这个WHERE子句有三个简单谓词:数据库
也能够认为是两个组合谓词:bash
关系型数据库的一大优点就是,用户无须关系数据的访问方式。其访问路径是由DBMS的一个组件,即优化器来肯定的。优化器是SQL处理过程的核心。服务器
这里以mysql为例展现一个简单的mysql服务器逻辑结构性能
在SQL语句可以被真正执行以前,优化器必须首先肯定如何访问数据。好比mysql会解析查询并建立解析树,而后对其进行各类优化,包括决定选择合适的索引,决定表的读取顺序。优化
而谓词表达式是索引设计的主要入手点。若是一个索引可以知足SELECT查询语句的全部谓词表达式,那么优化器就颇有可能创建起一个高效的访问路径。spa
当索引时以B+树的形式组织,若是有谓词表达式WHERE A > 100 AND A < 110
,那么查询到的叶子节点的范围会最终为下图:设计
图的左边是索引的一个窄片断,咱们称这个片断为索引片。这个片断会被顺序扫描,上面索引行的值在100到110之间,相应的表行将经过同步读从表(也可能在缓冲池)中读取。3d
因此访问路径的成本很大程度上取决于这个索引片的厚度,也就是谓词表达式肯定的值域范围。索引片越厚,须要扫描的索引页就越多,须要处理的索引记录也就越多,但最大的开销仍是来自于增长的对表的同步读操做,每次表页读取的I/O操做可能须要10ms。相应的,索引片比较窄,就会减小对表的同步读取。
并非全部的索引列都可以定义索引片的大小。有时候,列可能既存在于WHERE子句中,也存在于索引中,但这个列却不能参与索引片的定义,举个例子。 表上有一联合索引(A,B,C,D),有以下sql语句:
WHERE A = :A
AND
B > :B
AND
C = :C
复制代码
咱们须要肯定WHERE子句中的谓词是否可以肯定索引片大小:
总结:
上述WHERE子句有两个匹配列,列A和列B,他们定义了扫描的索引片。除此以外还有一个列C做为过滤列。因此只有当一行同时知足这三个谓词时才会访问表中的数据。
若是列B的谓词表达式是等值谓词,那么这三个列均可以做为匹配列。
若是取消列A的谓词表达式,那么索引片断就是整个索引的大小,列B和列C都仅仅只能用来过滤。
过滤因子描述的谓词的选择性,即表中知足谓词条件的记录行数所占的比例,它主要依赖于列值的分布状况。
计算过滤因子的公式为:
结果集数量/表行的数量
好比咱们的一张用户表里有SEX这个字段,当加入一个女性用户,SEX=‘F’的过滤因子就会变大。
若是男性在表中占70%,那么SEX='M'的过滤因子就是70%,SEX='F'的过滤因子为30%,SEX列的最差状况下过滤因子为70%,平均过滤因子为50%。
若是男女比例一比一,那么列SEX最差状况下的过滤因子和平均过滤因子都是50%。
咱们在评估一个索引是否合适的时候,最差状况下的过滤因子比平均过滤因子更重要,由于最差状况与最差输入相关,即在该输入条件下,基于特定索引的查询将消耗最长的时间。
那咱们如何来计算三组合谓词表达式的过滤因子呢?
若是组成谓词的列之间非相关,那么组合谓词的过滤因子能够从单个谓词的过滤因子推导出来。
非相关的意思是两个谓词的值互不影响,例如咱们有一张user表,里面有"province"和"city"两个字段,那这就是两个相关的谓词,由于城市的值必须是他所在的省下的城市。而CITY和BD(生日)就是不相关的谓词。
好比组合谓词 CITY = :CITY AND BD = :BD
的过滤因子等于谓词 CITY = :CITY
和谓词 BD = :BD
的过滤因子的乘积。
若是列CITY有2000个不一样的值,列BD有2700个不一样的值,那么组合谓词的过滤因子就是:1/2000*1/2700
。那么列组合[CITY,BD]总共有5400000个不一样的值。
而对于有相关性的列,值会比这小不少。
咱们在设计索引结构的时候,须要将SQL语句中的组合谓词看作一个总体来评估过滤因子。
很显然,须要扫描的索引片的大小对访问路径的性能影响相当重要。过滤因子越小,筛选出来的索引片的就越小,那就意味着访问表的次数越少。
假设表有联合索引 (MAKE, MODEL, YEAR)
对于sql语句:
SELECT PRICE, COLOR, DEALERNO
FROM CAR
WHERE MAKE = :MAKE
AND
MODEL = :MODEL
ORDER BY PRICE
复制代码
MAKE 和 MODEL都是匹配列。若是组合谓词的过滤因子是0.1%,那么所须要访问的索引片大小将为整个索引的0.1%。
而对于下面这个sql语句,这个索引就不大好了:
SELECT PRICE, COLOR, DEALERNO
FROM AUTO
WHERE MAKE = :MAKE
AND
YEAR = :YEAR
复制代码
因为联合索引的最左匹配原则,匹配列只有MAKE。过滤因子为1%,索引片比较大。
sql语句:
SELECT LNAME, FNAME, CNO
FROM CUST
WHERE SEX='M'
AND
(WEIGHT > 90
OR
HEIGHT > 190)
ORDER BY LNAME, FNAME
复制代码
这个SQL语句查找身材高大有必定要求的男性,此时匹配谓词只有一个SEX,过滤因子正常状况下为50%,若是表有100万行记录,那么索引片就有50万行,这就是至关厚的索引片了。
思考一下为如下两个SQL语句设计最佳的索引
SELECT LNAME, FNAME, CNO
FROM CUST
WHERE SEX = 'M'
AND
HEIGHT > 190
ORDER BY LNAME, FNAME
复制代码
SELECT LNAME, FNAME, CNO
FROM CUST
WHERE SEX = 'M'
AND
(WHIGHT > 90
OR
HEIGHT > 190)
ORDER BY LNAME, FNAME
复制代码