数据库索引的优化及SQL处理过程
想要设计出好的索引,首先必须了解SQL语句在数据库服务器中的处理过程,本文介绍数据库索引设计与优化中几个对索引优化非常重要的概念。
谓词
谓词就是条件表达式。 SQL语句的where子句由一个或者多个谓词组成。
WHERE SEX = 'M'
AND
(WHIGHT > 90
OR
HEIGHT > 190)
上面这个WHERE子句有三个简单谓词:
- SEX = ‘M’
- WRIGHT > 90
- HEIGHT >190
也可以认为是两个组合谓词:
- WEIGHT > 90 OR HEIGHT >190
- SEX = ‘M’ AND (WEIGHT > 90 OR HEIGHT >190)
优化器及访问路径
关系型数据库的一大优势就是,用户无须关系数据的访问方式。其访问路径是由DBMS的一个组件,即优化器来确定的。优化器是SQL处理过程的核心。
这里以mysql为例展示一个简单的mysql服务器逻辑结构

在图中我们可以看到优化器的位置。
在SQL语句能够被真正执行之前,优化器必须首先确定如何访问数据。比如mysql会解析查询并创建解析树,然后对其进行各种优化,包括决定选择合适的索引,决定表的读取顺序。
而谓词表达式是索引设计的主要入手点。如果一个索引能够满足SELECT查询语句的所有谓词表达式,那么优化器就很有可能建立起一个高效的访问路径。
索引片及匹配列
如果索引时以B+树的形式组织的,如果有谓词表达式WHERE A > 100 AND A < 110,那么查询到的叶子节点的范围会最终为下图:

图的左边是索引的一个窄片段,我们称这个片段为索引片。这个片段会被顺序扫描,上面索引行的值在100到110之间,相应的表行将通过同步读从表(也可能在缓冲池)中读取。
所以访问路径的成本很大程度上取决于这个索引片的厚度,也就是谓词表达式确定的值域范围。索引片越厚,需要扫描的索引页就越多,需要处理的索引记录也就越多,但最大的开销还是来自于增加的对表的同步读操作,每次表页读取的I/O操作可能需要10ms。相应的,索引片比较窄,就会减少对表的同步读取。
索引过滤及过滤列
并不是所有的索引列都能够定义索引片的大小。有时候,列可能既存在于WHERE子句中,也存在于索引中,但这个列却不能参与索引片的定义,举个例子。 表上有一联合索引(A,B,C,D),有如下sql语句:
WHERE A = :A
AND
B > :B
AND
C = :C
我们需要确定WHERE子句中的谓词是否能够确定索引片大小:
- 首先我们看在WHERE子句中,该列是否至少有一个足够简单的谓词与之对应? 如果有,那么这个列就是匹配列。如果没有,那么这个列及其后面的索引列都是非匹配列。
- 如果该谓词是一个范围谓词,那么剩余的索引列都是非匹配列。
- 对于最后一个匹配列之后的索引列,如果拥有一个足够简单的谓词与其对应,那么该列为过滤列。 根据这个方法,我们可以判断出列A出现在一个等值谓词中,这是一个足够简单的谓词,因此A是匹配列,列B是一个范围谓词,也是匹配列。而B后面的列C无法定义索引片(无法让索引片变得更窄),但它依旧可以参与索引片的过滤过程。 也就是说我们通过列A和列B定义了索引片的大小,而列C不能,但是在访问表之前,依旧可以通过列C来过滤记录,能够减少不必要的表访问。列C就属于过滤列,它和列A列B一样重要。
总结:
上述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
数据库索引的优化及SQL处理过程的更多相关文章
- 大数据量数据库设计与优化方案(SQL优化)
转自:http://blog.sina.com.cn/s/blog_6c0541d50102wxen.html 一.数据库结构的设计 如果不能设计一个合理的数据库模型,不仅会增加客户端和服务器段程序的 ...
- Oracle学习总结(7)—— 常用的数据库索引优化语句总结
不管是用C/C++/Java等代码编写的程序,还是SQL编写的数据库脚本,都存在一个持续优化的过程.也就是说,代码优化对于程序员来说,是一个永恒的话题. 近期,我们对之前编写的数据库脚本进行了全面的自 ...
- 索引优化、Sql查询语句优化
工作中我们经常会遇到系统查询慢的情况,一般我们会采取好多方法进行优化,如建立索引,优化查询Sql,分表,规范数据表结构设计,调整数据库参数(内存分配.缓存等),增加硬件配置,优化网络环境等.下面介绍两 ...
- 数据库性能优化:SQL索引
SQL索引在数据库优化中占有一个非常大的比例, 一个好的索引的设计,可以让你的效率提高几十甚至几百倍,在这里将带你一步步揭开他的神秘面纱. 1.1 什么是索引? SQL索引有两种,聚集索引和非聚集索引 ...
- MySql数据表设计,索引优化,SQL优化,其他数据库
MySql数据表设计,索引优化,SQL优化,其他数据库 1.数据表设计 1.1数据类型 1.2避免空值 1.3text类型优化 2.索引优化 2.1索引分类 2.2索引优化 3.SQL优化 3.1分批 ...
- SQL语句优化、mysql不走索引的原因、数据库索引的设计原则
SQL语句优化 1 企业SQL优化思路 1.把一个大的不使用索引的SQL语句按照功能进行拆分 2.长的SQL语句无法使用索引,能不能变成2条短的SQL语句让它分别使用上索引. 3.对SQL语句功能的拆 ...
- Oracle数据库该如何着手优化一个SQL
这是个终极问题,因为优化本身的复杂性实在是难以总结的,很多时候优化的方法并不是用到了什么高深莫测的技术,而只是一个思想意识层面的差异,而这些都很可能连带导致性能表现上的巨大差异. 所以有时候我们应该先 ...
- SQL Server数据库性能优化之SQL语句篇【转】
SQL Server数据库性能优化之SQL语句篇http://www.blogjava.net/allen-zhe/archive/2010/07/23/326927.html 近期项目需要, 做了一 ...
- SQL Server调优系列进阶篇(如何维护数据库索引)
前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常 ...
随机推荐
- ASI中POST请求和文件下载
//POST请求 1 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // 1.URL NSURL *url = ...
- 华为云WeLink:智能工作空间,联接无限想象
[中国,上海,2019年9月19日] 在HUAWEI CONNECT 2019期间,华为办公应用装备部部长王俊先生代表华为云介绍WeLink--企业专属的智能工作空间.WeLink源于华为数字化办公实 ...
- 深入比特币原理(四)——锁定脚本(locking script)与解锁脚本(unlocking script)
通常比特币都是以虚拟货币的概念出现在大众眼前,实际上比特币是第一个真正的区块链"平台",利用它去中心化.不可篡改.可追溯等特点不光可以实现多种交易模式(如点对点交易.多重签名交易等 ...
- 时至今日,我们应该承认.Net目前的状况实在堪忧
一: .Net之前 .Net 经历了多年的锤炼,语言特性本身非常优雅和完善,也是非常甜品的一种语言 二: .Net现状 但是与此同时,.Net的生态日益糟糕,困扰着广大.Neter 三: .N ...
- hoverdir
js 引入 jq && modernizr.custom.97074.js &&jquery.hoverdir.js css 引入style.css html代 ...
- [TimLinux] Python 装饰器
1. 装饰器 一种语法格式,用于替换另外一个编码风格,一种语法糖,通过语法结构明确标识出这样一种语法. 自动在被装饰对象尾部执行代码(不使用装饰器语法时,需要明确写明的代码) 被装饰对象可以为函数.类 ...
- C++fread小解
std::fread ——C++ 输入/输出库 C 风格 I/O 定义于头文件 <cstdio> std::size_t fread( void* buffer, std::size_t ...
- cmake 简介
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程).他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的 ...
- socket AcceptAsync方法的使用
AcceptAsync与Accept很大的不一样 Accept是一个同步 阻塞的已经封装好底层的方法 AcceptAsync是一个异步 非阻塞未封装的底层连接入口,需要手动填入连接代码用于优化sock ...
- 学习ES6笔记──工作中常用到的ES6语法
学习博客:https://segmentfault.com/a/1190000016068235