mysql 学习 - 掌握单表查询优化

MySQL Server有一个称为查询优化器的模块,一条查询语句进行语法解析之后就会被交给查询优化器来进行优化,优化的结果就是生成一个所谓的执行计划,这个执行计划表明了应该使用哪些索引进行查询,表之间的连接顺序是啥样的,最后会按照执行计划中的步骤调用存储引擎提供的方法来真正的执行查询,并将查询结果返回给用户。但是在介绍查询优化器之前, 我们需要先对单表查询的优化进行一定程度上的掌握.

建表

CREATE TABLE single_table (
    id INT NOT NULL AUTO_INCREMENT,
    key1 VARCHAR(100),
    key2 INT,
    key3 VARCHAR(100),
    key_part1 VARCHAR(100),
    key_part2 VARCHAR(100),
    key_part3 VARCHAR(100),
    common_field VARCHAR(100),
    PRIMARY KEY (id),
    KEY idx_key1 (key1),
    UNIQUE KEY idx_key2 (key2),
    KEY idx_key3 (key3),
    KEY idx_key_part(key_part1, key_part2, key_part3)
) Engine=InnoDB CHARSET=utf8;

访问方法和访问类型

MySQL查询器在执行查询语句的方式称之为访问方法或者访问类型

访问方法 const

通过主键列来定位一条记录,比方说这个查询:

SELECT * FROM single_table WHERE id = 1438;

根据唯一二级索引列来定位一条记录的速度也是贼快的,比如下边这个查询:

SELECT * FROM single_table WHERE key2 = 3841;

第一步先从idx_key2对应的B+树索引中根据key2列与常数的等值比较条件定位到一条二级索引记录,然后再根据该记录的id值到聚簇索引中获取到完整的用户记录。

mysql 认为通过主键或者唯一二级索引列与常数的等值比较来定位一条记录是像坐火箭一样快的,所以他们把这种通过主键或者唯一二级索引列来定位一条记录的访问方法定义为:const,意思是常数级别的,代价是可以忽略不计的。

访问方法 ref

对某个普通的二级索引列与常数进行等值比较,比如这样:

SELECT * FROM single_table WHERE key1 = ‘abc‘;

由于普通二级索引并不限制索引列值的唯一性,所以可能找到多条对应的记录,也就是说使用二级索引来执行查询的代价取决于等值匹配到的二级索引记录条数。如果匹配的记录较少,则回表的代价还是比较低的,所以MySQL可能选择使用索引而不是全表扫描的方式来执行查询。mysql 就把这种搜索条件为二级索引列与常数等值比较,采用二级索引来执行查询的访问方法称为:ref.

二级索引列值为NULL的情况

不论是普通的二级索引,还是唯一二级索引,它们的索引列对包含NULL值的数量并不限制,所以我们采用key IS NULL这种形式的搜索条件最多只能使用ref的访问方法,而不是const的访问方法。

二级索引为联合索引, 并且都是等值比较时的情况

对于某个包含多个索引列的二级索引来说,只要是最左边的连续索引列是与常数的等值比较就可能采用ref的访问方法,比方说下边这几个查询:

SELECT * FROM single_table WHERE key_part1 = ‘god like‘;
SELECT * FROM single_table WHERE key_part1 = ‘god like‘ AND key_part2 = ‘legendary‘;
SELECT * FROM single_table WHERE key_part1 = ‘god like‘ AND key_part2 = ‘legendary‘ AND key_part3 = ‘penta kill‘;

但是如果最左边的连续索引列并不全部是等值比较的话,它的访问方法就不能称为ref了,比方说这样:

SELECT * FROM single_table WHERE key_part1 = ‘god like‘ AND key_part2 > ‘legendary‘;

访问方法 ref_or_null

有时候我们不仅想找出某个二级索引列的值等于某个常数的记录,还想把该列的值为NULL的记录也找出来,就像下边这个查询:

SELECT * FROM single_table WHERE key1 = ‘abc‘ OR key1 IS NULL;

当使用二级索引而不是全表扫描的方式执行该查询时,这种类型的查询使用的访问方法就称为ref_or_null,先分别从idx_key1索引对应的B+树中找出key1 IS NULL和key1 = ‘abc‘的两个连续的记录范围,然后根据这些二级索引记录中的id值再回表查找完整的用户记录。

访问方法 range

有时候我们面对的搜索条件更复杂,比如下边这个查询:

SELECT * FROM single_table WHERE key2 IN (1438, 6328) OR (key2 >= 38 AND key2 <= 79);

如果采用二级索引 + 回表的方式来执行的话,那么此时的搜索条件就不只是要求索引列与常数的等值匹配了,而是索引列需要匹配某个或某些范围的值,在本查询中key2列的值只要匹配下列3个范围中的任何一个就算是匹配成功了:

key2的值是1438
key2的值是6328
key2的值在38和79之间

mysql 把这种利用索引进行范围匹配的访问方法称之为:range.

访问方法 index

看下边这个查询:

SELECT key_part1, key_part2, key_part3 FROM single_table WHERE key_part2 = ‘abc‘;

由于key_part2并不是联合索引idx_key_part最左索引列,所以我们无法使用ref或者range访问方法来执行这个语句。但是这个查询符合下边这两个条件:

它的查询列表只有3个列:key_part1, key_part2, key_part3,而索引idx_key_part又包含这三个列。
搜索条件中只有key_part2列。这个列也包含在索引idx_key_part中。

也就是说我们可以直接通过遍历idx_key_part索引的叶子节点的记录来比较key_part2 = ‘abc‘这个条件是否成立,把匹配成功的二级索引记录的key_part1, key_part2, key_part3列的值直接加到结果集中就行了。由于二级索引记录比聚簇索记录小的多(聚簇索引记录要存储所有用户定义的列以及所谓的隐藏列,而二级索引记录只需要存放索引列和主键),而且这个过程也不用进行回表操作,所以直接遍历二级索引比直接遍历聚簇索引的成本要小很多,MySQL 就把这种采用遍历二级索引记录的执行方式称之为:index。

all

最直接的查询执行方式就是我们已经提了无数遍的全表扫描,对于InnoDB表来说也就是直接扫描聚簇索引,MySQL把这种使用全表扫描执行查询的方式称之为:all。

注意事项

尽量少的回表

下边的这个查询:

SELECT * FROM single_table WHERE key1 = ‘abc‘ AND key2 > 1000;

查询优化器会识别到这个查询中的两个搜索条件:

key1 = ‘abc‘
key2 > 1000

优化器一般会根据single_table表的统计数据来判断到底使用哪个条件到对应的二级索引中查询扫描的行数会更少,选择那个扫描行数较少的条件到对应的二级索引中查询.

一个使用到索引的搜索条件和没有使用该索引的搜索条件使用OR连接起来后是无法使用该索引的

SELECT * FROM single_table WHERE key2 > 100 OR common_field = ‘abc‘;

我们把使用不到idx_key2索引的搜索条件替换为TRUE:

SELECT * FROM single_table WHERE key2 > 100 OR TRUE;

接着化简:

SELECT * FROM single_table WHERE TRUE;

索引合并

Intersection合并

MySQL在某些特定的情况下才可能会使用到Intersection索引合并:

情况一:二级索引列是等值匹配的情况,对于联合索引来说,在联合索引中的每个列都必须等值匹配,不能出现只匹配部分列的情况。


情况二:主键列可以是范围匹配

Union合并

Intersection是交集的意思,这适用于使用不同索引的搜索条件之间使用AND连接起来的情况;Union是并集的意思,适用于使用不同索引的搜索条件之间使用OR连接起来的情况。与Intersection索引合并类似,MySQL在某些特定的情况下才可能会使用到Union索引合并:

情况一:二级索引列是等值匹配的情况,对于联合索引来说,在联合索引中的每个列都必须等值匹配,不能出现只出现匹配部分列的情况。


情况二:主键列可以是范围匹配


情况三:使用Intersection索引合并的搜索条件

查询条件符合了这些情况也不一定就会采用Union索引合并,也得看优化器的心情。优化器只有在单独根据搜索条件从某个二级索引中获取的记录数比较少,通过Union索引合并后进行访问的代价比全表扫描更小时才会使用Union索引合并。

Sort-Union合并

Union索引合并的使用条件太苛刻,必须保证各个二级索引列在进行等值匹配的条件下才可能被用到,比方说下边这个查询就无法使用到Union索引合并:

SELECT * FROM single_table WHERE key1 < ‘a‘ OR key3 > ‘z‘

这是因为根据key1 < ‘a‘从idx_key1索引中获取的二级索引记录的主键值不是排好序的,根据key3 > ‘z‘从idx_key3索引中获取的二级索引记录的主键值也不是排好序的,但是key1 < ‘a‘和key3 > ‘z‘这两个条件又特别让我们动心,所以我们可以这样:

先根据key1 < ‘a‘条件从idx_key1二级索引中获取记录,并按照记录的主键值进行排序


再根据key3 > ‘z‘条件从idx_key3二级索引中获取记录,并按照记录的主键值进行排序


因为上述的两个二级索引主键值都是排好序的,剩下的操作和Union索引合并方式就一样了。

联合索引替代Intersection索引合并

SELECT * FROM single_table WHERE key1 = ‘a‘ AND key3 = ‘b‘;

这个查询之所以可能使用Intersection索引合并的方式执行,还不是因为idx_key1和idx_key3是两个单独的B+树索引,你要是把这两个列搞一个联合索引,那直接使用这个联合索引就把事情搞定了,何必用啥索引合并呢,就像这样:

ALTER TABLE single_table drop index idx_key1, idx_key3, add index idx_key1_key3(key1, key3);

这样我们把没用的idx_key1、idx_key3都干掉,再添加一个联合索引idx_key1_key3,使用这个联合索引进行查询简直是又快又好,既不用多读一棵B+树,也不用合并结果. 不过小心有单独对key3列进行查询的业务场景,这样子不得不再把key3列的单独索引给加上。

原文地址:https://www.cnblogs.com/it-dennis/p/12623057.html

时间: 2024-11-09 21:16:19

mysql 学习 - 掌握单表查询优化的相关文章

MySQL学习9 - 单表查询

一.单表查询的语法 二.关键字的执行优先级(重点) 三.单表查询示例 1.where约束 2.group by分组查询 3.聚合函数 4.HAVING过滤 5.order by查询排序 6.limit 限制查询的记录数 一.单表查询的语法 SELECT 字段1,字段2... FROM 表名 WHERE 条件 GROUP BY field HAVING 筛选 ORDER BY field LIMIT 限制条数 二.关键字的执行优先级(重点) 重点中的重点:关键字的执行优先级 from where

「mysql优化专题」单表查询优化的一些小总结,非索引设计(3)

单表查询优化:(关于索引,后面再开单章讲解) (0)可以先使用 EXPLAIN 关键字可以让你知道MySQL是如何处理你的SQL语句的.这可以帮我们分析是查询语句或是表结构的性能瓶颈. (1)写sql要明确需要的字段,要多少就写多少字段,而不是滥用 select * (2)可以用使用连接(JOIN)来代替子查询 (3)使用分页语句:limit start , count 或者条件 where子句时,有什么可限制的条件尽量加上,查一条就limit一条.做到不滥用.比如说我之前做过的的p2p项目,只

MySQL学习笔记-操作数据表中的记录

MySQL学习笔记-操作数据表中的记录 1.插入记录 INSERT INSERT [INTO] tbl_name [(col_name,...)] {VAULES|VALUE} ({expr|DEFAULT},...),(...),... 例,插入单条记录: mysql> USE testDatabase changed mysql> CREATE TABLE users(    -> id SMALLINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,   

mysql简单的单表查询详解

mysql简单的单表查询详解 MySQL的查询操作: 单表查询:简单查询 多表查询:连续查询 联合查询: 选择和投影: 投影:挑选要显示的字段 选择:挑选符合条件的行 投影:SELECT 字段1, 字段2, ... FROM tb_name;  SELECT * FROM tb_name; 选择:SELECT 字段1, ... FROM tb_name WHERE 子句; 布尔条件表达式 mysql> CREATE TABLE students (SID INT UNSIGNED AUTO_IN

MySQL学习笔记之三 表类型

你能用的数据库引擎取决于MySQL在安装时候是如何被编译的.要添加一个新的引擎,就必须编译MySQL.仅仅为了添加一个特性而编译应用程序的想法对于Windows的开发人员来说可能有点小题大做,得不偿失,但是在Unix的世界里,这已经成为了标准.在缺省的情况下,MySQL支持三个引擎:ISAM.MyISAM和HEAP.另外两种类型InnoDB和Berkley(BDB),也常常可以使用. ISAM ISAM是一个定义明确且历经时间考验的数据表格管理方法,它在设计之初就考虑到数据库被查询的次数远远大于

MySQL学习总结(五) --- 表数据查询

查询数据记录,是指从数据库对象表中获取所要查询的数据记录,该操作可以说是数据最基本的操作之一,也是使用频率最高.最重要的数据操作. 1.单表数据记录查询 1.1.简单数据查询 SELECT field1,field2,…,fieldn FROM table_name,filed可以是表中所有的字段,也可以是部分字段. SELECT * FROM table_name ,从表中查询所有的数据.*符号可以代替所有的字段,但是这种方式不够灵活,只能按照表中字段的顺序固定进行显示,不能够随便改变字段的显

MySQL 整库单表备份 for python脚本

#!/usr/bin/python#! _*_ coding:utf-8 _*_ from datetime import date, datetime, timedeltaimport os,time user = 'dbuser'passw = 'password'ip = '10.8.8.8'dir = '/data/backup/db/tmp'mysqlbase = '/usr/local/mysql/bin/' if  os.path.isdir(dir):    print("dir

Python中使用SQLAlchemy连接Mysql数据库(单表操作)

一,SQLAlchemy的安装 使用 $ easy_install sqlalchemy 或 $ pip install sqlalchemy 如果出现什么错,就进去root用户下进行安装试试,或者网上查查 >>> import sqlalchemy >>> 这样说明成功了,切记是小写哦 二,使用 理论我也不懂,自己查查资料,现在用一个小的案例说一下使用步骤 1,在进行数据操作之前要先连上数据库. >>> from sqlalchemy import

Hadoop 学习之单表连接

我在学习hadoop, 在看 陆嘉恒编著的hadoop实战,其中有单表连接的程序,我现在整理一下思路.这个问题是课本上的例子. 给出 child-parent 表, 要求输出 grandchild-grandparent 表 样例输入: child parent Tom Lucy Tom Jack Jone Lucy Jone Jack Lucy Mary Lucy Ben Jack Alice Jack Jesee Terry Alice Terry Jesee Philip Terry Ph