目的

MySQL查询优化器是基于代价(cost-based)的查询方式。因此,在查询过程中,最重要的一部分是根据查询的SQL语句,依据多种索引,计算查询需要的代价,从而选择最优的索引方式生成查询计划。

然而,在分析MySQL查询优化器过程中,是以查询优化器主线的原则进行研究,而忽略了很多细节的内容。因此,对MySQL索引选择进行进一步的研究和分析,给出创建和使用索引的规则,从而有助于分析SQL查询。

设计

设计原则主要依据为尽可能的测试索引,而不考虑索引的合理性。一方面可以更加全面的测试在多种索引存在的情况下,查询优化器是如何进行选择的。另一方面可以根据选择索引的原则,评估索引的合理性。

1、数据表设计

数据表设计如下所示,其中students_origin表中只有主键索引,students表设计主键索引(Primary

Key)、唯一索引(Unique Key)、B+索引、联合索引等。

点击(此处)折叠或打开

CREATE TABLE `students_origin` (

`id` int(11) NOT NULL,

`name` varchar(30) DEFAULT NULL,

`age` int(11) DEFAULT NULL,

`major` varchar(20) DEFAULT NULL,

`rank` int(11) DEFAULT NULL,

`total` int(11) DEFAULT NULL,

`comment` varchar(20) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `students` (

`id` int(11) NOT NULL,

`name` varchar(30) DEFAULT NULL,

`age` int(11) DEFAULT NULL,

`major` varchar(20) DEFAULT NULL,

`rank` int(11) DEFAULT NULL,

`total` int(11) DEFAULT NULL,

`comment` varchar(20) DEFAULT NULL,

PRIMARY KEY (`id`),

UNIQUE KEY `name` (`name`),

KEY `idx_major` (`major`),

KEY `idx_rank` (`rank`),

KEY `idx_rank_total` (`rank`,`total`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、测试语句设计

具体的查询SQL语句如下所示:

点击(此处)折叠或打开

1、select id from students;(students_origin)

2、select id, name from students; (students_origin)

3、select id, rank from students;

4、select id, rank, total from students;

5、select id, rank, total from students where rank = 2;

测试

1、主键查询

对主键id查询的测试,主要为了查看对于主键查询时,在多种索引并存的情况下,MySQL查询是如何执行的。以及如果使用了索引,选择索引的原则。

1.1源码分析

首先从源码角度分析的MySQL查询优化器的处理逻辑,从而了解MySQL是如何进行处理的。

MySQL查询优化器的核心处理逻辑,在JOIN::optimizer()函数(sql\sql_select.cc:854)中,通过基于代价的查询处理,选择代价最低查询方式执行。对于该查询来说,由于没有任何过滤条件,因此在调用make_join_statistics()函数(sql\sql_select.cc:2651)执行基于代价的查询处理过程后,查询执行的类型仍然为JT_ALL。也就是说,执行该查询仍然是全表扫描方式。特别说明,MySQL查询优化器基于代价的查询处理,主要是根据给出的查询条件来进行优化的,如where条件、ON条件等。

基于代价的查询处理之后,会根据查到的最优计划,生成最终的执行计划。该过程通过调用make_join_readinfo()函数(sql\sql_select.cc:6818)生成最终的查询计划。对于查询类型为JT_ALL,如果查询的表中有索引,则会调用find_shortest_key()函数(sql\sql_select.cc:13438)根据查询字段的索引覆盖情况,以及索引字段的键值长度,查找最短的键值,进行全表扫描。根据基于代价的查询处理之后,该查询的查询类型为JT_ALL。如果students中没有多个索引,只有主键索引的情况下,查询使用主键索引进行查询。但由于students表中有多个索引,因此,查找键值长度最短的索引idx_rank进行全表扫描。

find_shortest_key()函数的核心代码如下所示:

点击(此处)折叠或打开

for (uint nr=0; nr < table->s->keys ; nr++)

{

if (nr == usable_clustered_pk)

continue;

if (usable_keys->is_set(nr))

{

if (table->key_info[nr].key_length < min_length)

{

min_length=table->key_info[nr].key_length;

best=nr;

}

}

}

1.2执行计划

首先对仅有主键索引的students_origin表,执行SQL查询,查询计划如下所示:

id

select_type

table

type

possible_keys

key

key_len

ref

row

Extra

1

SIMPLE

students_origin

index

NULL

PRIMARY

4

NULL

10

Using index

对studentns数据表进行相同的SQL查询,查询的执行计划如下所示:

id

select_type

table

type

possible_keys

key

key_len

ref

row

Extra

1

SIMPLE

students

index

NULL

idx_rank

5

NULL

10

Using index

从执行计划来看,第一个查询的查询类型为index,键值为主键索引,而第二个查询使用idx_rank索引进行全表扫描。

对于相同的SQL查询语句,第二个查询使用的索引发生了变化。原因与Innodb数据和索引的存储有关,具体Innodb数据和索引的存储内容在MySQL官方文档和《High Performance MySQL》中都有相关的讲解。并且对于Innodb数据索引存储的源码实现逻辑,将进一步深入分析和测试,这里仅简要解释相关的内容。首先Innodb是聚集存储的,每条记录的数据以主键进行聚集存储,通过主键进行索引。如果没有定义主键,系统默认使用6个字符作为主键进行聚集存储。而辅助索引(secondary indexes)中的每条记录包含主键(primary key)和索引字段。

因此,对于该查询,查询优化器使用辅助索引进行查询,通过辅助索引就可以找到查询的id,查找的代价更低。

2、唯一索引查询

对唯一索引字段进行查询的测试,主要用于查看辅助索引在查询索引字段的情况下,MySQL选择索引的原则。

2.1源码分析

从源码分析MySQL查询优化器,与1.1的逻辑处理流程基本一致。由于没有任何过滤条件,因此查询优化器调用make_join_statistics()函数进行基于代价的查询过程后,查询类型为JT_ALL。在生成查询计划时,调用find_shortest_key()函数查找最短的键值,进行查询全表。由于唯一索引name能够被索引覆盖,因此通过唯一索引来查询全表。

2.2查询计划

对students_origin表执行SQL查询,查询计划如下所示:

id

select_type

table

type

possible_keys

key

key_len

ref

row

Extra

1

SIMPLE

students_origin

ALL

NULL

NULL

NULL

NULL

10

对students表执行SQL查询,查询计划如下所示:

id

select_type

table

type

possible_keys

key

key_len

ref

row

Extra

1

SIMPLE

students

index

NULL

name

93

NULL

10

Using index

由查询计划可知,对name字段没有唯一索引的表进行查询时,查询进行全表扫描,查询类型为ALL。而对name字段使用唯一索引的students表查询时,使用唯一索引进行全表扫描。查询类型为index,而非ALL。

通过以上分析可知,当查询的SQL可以通过辅助索引得到查询结果时,MySQL查询优化器会选择辅助索引进行查询优化,这有效的降低了查询的代价。

3、多个索引查询

对索引字段创建索引和联合索引,查看MySQL查询优化器是如何选择索引,来提高查询效率的。

3.1源码分析

从源码分析来看,MySQL查询优化器的逻辑处理流程与1.1基本一致。对于全表查询,make_join_statistics()函数进行基于代价的查询处理后,查询类型为JT_ALL。由于rank字段有两个索引,在调用find_shortest_key()函数,查找idx_rank(长度为5)和idx_rank_total(长度为10)中最短的键值,最终选择idx_rank进行查询。由于查询字段id, rank能够被索引覆盖,因此通过idx_rank索引来查询。

3.2查询计划

对students表执行SQL查询,查询计划如下所示:

id

select_type

table

type

possible_keys

key

key_len

ref

row

Extra

1

SIMPLE

students

index

NULL

idx_rank

5

NULL

10

Using index

从查询计划来看,在rank被idx_rank和idx_rank_total两个索引都覆盖的情况下,MySQL查询优化器选择键值长度短的idx_rank进行查询。

4、联合索引查询

使用联合索引,主要用于对比在查询中使用过滤条件时,选择索引的不同,从而分析MySQL查询优化器的处理逻辑。

4.1源码分析

从源码分析来看,MySQL查询优化器的逻辑处理流程与3.1基本一致。不同之处在于调用find_shortest_key()函数时,仅有idx_rank_total索引覆盖查询字段。因此查询优化器选择idx_rank_total索引进行覆盖索引查询。

4.2查询计划

对students表执行SQL查询,查询计划如下所示:

id

select_type

table

type

possible_keys

key

key_len

ref

row

Extra

1

SIMPLE

students

index

NULL

idx_rank_total

10

NULL

10

Using index

从查询计划来看,MySQL查询优化器使用覆盖索引idx_rank_total进行查询,该查询测试主要用于对比测试5中的结果。

5、条件索引查询

通过where条件,对查询内容进行过滤。该测试主要用于查看,rank在where过滤条件中时,与没有过滤条件的测试4进行比较,查看查询优化器选择索引的原则。

5.1源码分析

通过源码可知,对于有where过滤条件的SQL查询,查询优化器在make_join_statistics()函数中,根据基于代价的原则,通过调用update_ref_and_keys()函数(sql\sql_select.cc:3963),查找rank可以使用的索引有idx_rank和idx_rank_total两个索引,然后调用check_quick_keys()函数(sql\opt_range.cc:7628)查找对查询条件索引的记录数,MySQL查询优化器选择查询代价最低的索引idx_rank。查询优化器通过基于代价的处理后,查询类型为JT_REF。因此,生成查询计划时,由于已经选定了使用的索引,所以不必再调用find_shortest_key()函数再去查找键值最短的索引。

5.2查询计划

对students表执行SQL查询,查询计划如下所示:

id

select_type

table

type

possible_keys

key

key_len

ref

row

Extra

1

SIMPLE

students

ref

idx_rank,idx_rank_total

idx_rank

5

const

1

Using where

从查询计划来看,查询类型为ref,查询使用的索引为idx_rank,并且查询使用的是where条件,而没有使用索引覆盖进行查询。也就是说,首先通过索引idx_rank查找到键值,通过主键id查找对应的记录。

与测试4.2相比,查询计划发生了改变。主要原因是由于where条件改变了查询优化器的处理逻辑。查询优化器根据查询条件通过基于代价的处理,选择查询代价最低的索引,从而生成查询计划。而与4.2相比,查询优化器选择通过覆盖索引来进行查询,而不进行全表扫描。

在实际应用中,某些情况下,通过where条件进行基于代价的处理,选择where条件字段的索引,反而不如通过覆盖索引进行过滤where条件的查询代价低。正如测试5的查询,如果查询的字段可以通过覆盖索引进行查询,并通过where条件过滤的方式,可能比查询优化器通过where条件进行查找最优的索引查找,再通过对应主键进行查询更高效。该问题已超出范围,将在之后进行详细测试和分析。

结论

通过以上测试,对MySQL查询优化器选择索引的原则有较深入的理解,通过对SQL查询进行分析,有助于判断查询类型和选择的索引。

MySQL查询优化器的在索引选择的规则可以概括为:

1、对无过滤条件、索引可以覆盖的查询。查询优化器选择覆盖索引键值最短的索引进行查询;

2、对无过滤条件、无索引覆盖的查询。查询优化器选择全表扫描;

3、对有过滤条件、索引可以覆盖的查询。查询优化器优先基于代价的方式对过滤条件进行处理。如果可以索引查找,将选择代价最低的索引进行查找。如果是全表扫描,则通过查找键值最短的覆盖索引进行查询,并通过过滤条件进行过滤。

4、对有过滤条件、无索引覆盖的查询。查询优化器基于代价的方式对过滤条件进行处理,生成查询计划。

参考

1、secondary index:

2、Clustered and

Secondary Indexes:

mysql索引选择_MySQL 索引选择原则相关推荐

  1. mysql选择索引逻辑_Mysql索引选择逻辑

    有时候我们会发现mysql可能出现选错索引的情况,要了解这个问题我们得先看看sql优化器是怎么选择索引的 索引选择逻辑 优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句.在数据 ...

  2. mysql多索引结构_MySQL 索引结构

    谈到 MYSQL 索引服务端的同学应该是熟悉的不能再熟悉,新建表的时候怎么着都知道先来个主键索引,对于经常查询的列也会加个索引加快查询速度.那么 MYSQL 索引都有哪些类型呢?索引结构是什么样的呢? ...

  3. mysql 索引 原理_MySQL索引实现原理分析

    目前大部分数据库系统及文件系统都采用B-Tree(B树)或其变种B+Tree(B+树)作为索引结构.B+Tree是数据库系统实现索引的首选数据结构.在MySQL中,索引属于存储引擎级别的概念,不同存储 ...

  4. mysql精讲_Mysql 索引精讲

    开门见山,直接上图,下面的思维导图即是现在要讲的内容,可以先有个印象- 常见索引类型(实现层面) 索引种类(应用层面) 聚簇索引与非聚簇索引 覆盖索引 最佳索引使用策略 1.常见索引类型(实现层面) ...

  5. MySQL建立的索引看_MYSQL索引问题:索引在查询中如何使用?看了很多资料都只说索引的建立。是否建立了就不用再理会?...

    # 有这样一个表 P mysql> create table P (id int primary key, name varchar(10) not null, sex varchar(1), ...

  6. mysql索引实例_mysql索引之十:Mysql 索引案例学习

    理解索引最好的办法是结合示例,所以这里准备了一个索引的案例. 假设要设计一个在线约会网站,用户信息表有很多列,包裹国家,地区,城市,性别,眼睛颜色,等等.完整必须支持上面这些特征的各种组合来搜索用户, ...

  7. mysql fulltext类型_mysql索引类型:FULLTEXT、NORMAL、SPATIAL、UNIQUE的详细介绍(转)

    Normal 普通索引 表示普通索引,大多数情况下都可以使用 Unique 唯一索引 表示唯一的,不允许重复的索引,如果该字段信息保证不会重复例如身份证号用作索引时,可设置为unique 约束唯一标识 ...

  8. 讴 mysql 首字母_MYSQL索引

    什么是索引? 举个例子:新华字典,有目录,有正文内容.索引就相当于目录,正文内容就相当于数据. 索引有什么用? 索引用于快速查找在某列中有一特定值的行. 一条查询语句,如果没有索引,将对全表进行扫描. ...

  9. mysql验证索引正确性_mysql索引测试

    生成测试数据: 创建表用于测试: /*创建t_user表用于数据测试*/ DROP TABLE IF EXISTS t_user; CREATE TABLE `t_user` ( `id` bigin ...

最新文章

  1. IIS支持http协议的:put、delete等方法
  2. LNK1169 找到一个或多个多重定义的符号
  3. 图卷积神经网络(part3)--三个经典谱域图卷积模型
  4. 非阻塞模式(ioctlsocket)
  5. python聚类分析结果很差_python中的聚类分析:value错误:x和y的大小必须相同
  6. 读书笔记:《时间投资法》之二
  7. ext拖动gridpanel的列组件消失_未来光伏组件市场格局:182mm市场占有率65%、210占5%、166占20%...
  8. 《Hack与HHVM权威指南》——1.1 为什么使用类型检查器
  9. C语言volatile的本质(三十四)
  10. JAVA 多用户商城系统b2b2c- 服务消费者(rest+ribbon)
  11. 干货:怎么提高科技成果转移转化成效?
  12. 最速下降法matlab全局最小值_matlab实现最速下降法和dfp求函数最小值
  13. 网赚在线之Cashfiesta公司网上赚钱全攻略
  14. 两台电脑实现串口通信
  15. 采样频率Hz 采样率KSPS或MSPS,两种单位的换算关系
  16. python中的search的group(0),group(1).........的方法
  17. python--批量离线安装python包
  18. Hybird A*算法
  19. linux安全(1)
  20. github忘记邮箱找回办法

热门文章

  1. h3c 链路聚合测试_4G/5G聚合路由器在直播中无线多链路聚合图传技术是什么?
  2. summernote使用实例,解决了小图标方框显示问题
  3. Python标准库中的uuid
  4. IDEA的Debug 控制台输出窗口没有显示
  5. Linux中Redis的安装
  6. c语言prime函数怎么用_C语言教程_v20201106
  7. mariadb mysql 5.7_MariaDB 10.1 和 MySQL 5.7 在普通商用硬件上的表现
  8. fpga运算服务器_SparseArray替代HashMap来提高性能
  9. 计算机存储单位字的英语,计算机存储基本单位,位、字节、字以及KB、MB和GB怎么换算?...
  10. php getlastid,PHP-获取最后一个插入ID