千家信息网

MySQL查询优化的简介

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,这篇文章给大家分享的是有关MySQL查询优化的简介的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。前言MySQL是关系性数据库中的一种,查询功能强,数据一致性高,数据安全性高
千家信息网最后更新 2026年10月31日MySQL查询优化的简介

这篇文章给大家分享的是有关MySQL查询优化的简介的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

前言

MySQL是关系性数据库中的一种,查询功能强,数据一致性高,数据安全性高,支持二级索引。但性能方面稍逊于非关系性数据库,特别是百万级别以上的数据,很容易出现查询慢的现象。这时候需要分析查询慢的原因,一般情况下是程序员sql写的烂,或者是没有键索引,或者是索引失效等原因导致的。

这时候MySQL 提供的 EXPLAIN 命令就尤其重要, 它可以对 SELECT 语句进行分析, 并输出 SELECT 执行的详细信息, 以供开发人员针对性优化.

而且就在查询语句前加上 Explain 就成:

EXPLAIN SELECT * FROM customer WHERE id < 100;

准备

首先需要建立两个测试用表及数据:

CREATE TABLE `customer` (  `id` BIGINT(20) unsigned NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) NOT NULL DEFAULT '', `age` INT(11) unsigned DEFAULT NULL, PRIMARY KEY (`id`), KEY `name_index` (`name`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4INSERT INTO customer (name, age) VALUES ('a', 1);INSERT INTO customer (name, age) VALUES ('b', 2);INSERT INTO customer (name, age) VALUES ('c', 3);INSERT INTO customer (name, age) VALUES ('d', 4);INSERT INTO customer (name, age) VALUES ('e', 5);INSERT INTO customer (name, age) VALUES ('f', 6);INSERT INTO customer (name, age) VALUES ('g', 7);INSERT INTO customer (name, age) VALUES ('h', 8);INSERT INTO customer (name, age) VALUES ('i', 9);
CREATE TABLE `orders` ( `id` BIGINT(20) unsigned NOT NULL AUTO_INCREMENT, `user_id` BIGINT(20) unsigned NOT NULL DEFAULT 0, `product_name` VARCHAR(50) NOT NULL DEFAULT '', `productor` VARCHAR(30) NOT NULL DEFAULT '', PRIMARY KEY (`id`), KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4INSERT INTO orders (user_id, product_name, productor) VALUES (1, 'p1', 'WHH');INSERT INTO orders (user_id, product_name, productor) VALUES (1, 'p2', 'WL');INSERT INTO orders (user_id, product_name, productor) VALUES (1, 'p1', 'DX');INSERT INTO orders (user_id, product_name, productor) VALUES (2, 'p1', 'WHH');INSERT INTO orders (user_id, product_name, productor) VALUES (2, 'p5', 'WL');INSERT INTO orders (user_id, product_name, productor) VALUES (3, 'p3', 'MA');INSERT INTO orders (user_id, product_name, productor) VALUES (4, 'p1', 'WHH');INSERT INTO orders (user_id, product_name, productor) VALUES (6, 'p1', 'WHH');INSERT INTO orders (user_id, product_name, productor) VALUES (9, 'p8', 'TE');

EXPLAIN 输出格式

EXPLAIN 命令的输出内容大致如下:

mysql> explain select * from customer where id = 1\G*************************** 1. row *************************** id: 1 select_type: SIMPLE table: customer partitions: NULL type: constpossible_keys: PRIMARY key: PRIMARY key_len: 8 ref: const rows: 1 filtered: 100.00 Extra: NULL1 row in set, 1 warning (0.00 sec)

各列的含义如下:

  • id: SELECT 查询的标识符. 每个 SELECT 都会自动分配一个唯一的标识符.

  • select_type: SELECT 查询的类型.

  • table: 查询的是哪个表partitions: 匹配的分区type: join 类型

  • possible_keys: 此次查询中可能选用的索引

  • key: 此次查询中确切使用到的索引.

  • ref: 哪个字段或常数与 key 一起被使用

  • rows: 显示此查询一共扫描了多少行. 这个是一个估计值.

  • filtered: 表示此查询条件所过滤的数据的百分比

  • extra: 额外的信息

接下来我们来重点看一下比较重要的几个字段.

select_type

  • SIMPLE -- 简单的select 查询,查询中不包含子查询或者UNION

  • PRIMARY -- 查询中若包含任何复杂的子查询,最外层查询则被标记为primary

  • UNION -- 表示此查询是 UNION 的第二或随后的查询

  • DEPENDENT UNION -- UNION 中的第二个或后面的查询语句, 取决于外面的查询

  • UNION RESULT -- 从UNION表获取结果的select结果

  • DERIVED -- 在from列表中包含的子查询被标记为derived(衍生)MySQL会递归执行这些子查询,把结果放在临时表里。

  • SUBQUERY -- 在select或where 列表中包含了子查询

  • DEPENDENT SUBQUERY -- 子查询中的第一个 SELECT, 取决于外面的查询. 即子查询依赖于外层查询的结果.

最常见的查询类别应该是 SIMPLE 了, 比如当我们的查询没有子查询, 也没有 UNION 查询时, 那么通常就是 SIMPLE 类型, 例如:

mysql> explain select * from customer where id = 2\G*************************** 1. row *************************** id: 1 select_type: SIMPLE table: customer partitions: NULL type: constpossible_keys: PRIMARY key: PRIMARY key_len: 8 ref: const rows: 1 filtered: 100.00 Extra: NULL1 row in set, 1 warning (0.00 sec)

如果我们使用了 UNION 查询, 那么 EXPLAIN 输出 的结果类似如下:

mysql> EXPLAIN (SELECT * FROM customer WHERE id IN (1, 2, 3))  -> UNION -> (SELECT * FROM customer WHERE id IN (3, 4, 5));+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+| 1 | PRIMARY | customer | NULL | range | PRIMARY | PRIMARY | 8 | NULL | 3 | 100.00 | Using where || 2 | UNION | customer | NULL | range | PRIMARY | PRIMARY | 8 | NULL | 3 | 100.00 | Using where || NULL | UNION RESULT |  | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |+----+--------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-----------------+3 rows in set, 1 warning (0.00 sec)

table

表示查询涉及的表或衍生表

type

type 字段比较重要, 它提供了判断查询是否高效的重要依据依据. 通过 type 字段, 我们判断此次查询是 全表扫描 还是 索引扫描 等.

type 常用类型

type 常用的取值有:

  • system: 表中只有一条数据. 这个类型是特殊的 const 类型.

  • const: 针对主键或唯一索引的等值查询扫描, 最多只返回一行数据. const 查询速度非常快, 因为它仅仅读取一次即可.例如下面的这个查询, 它使用了主键索引, 因此 type 就是 const 类型的.

mysql> explain select * from customer where id = 2\G*************************** 1. row *************************** id: 1 select_type: SIMPLE table: customer partitions: NULL type: constpossible_keys: PRIMARY key: PRIMARY key_len: 8 ref: const rows: 1 filtered: 100.00 Extra: NULL1 row in set, 1 warning (0.00 sec)

eq_ref: 此类型通常出现在多表的 join 查询, 表示对于前表的每一个结果, 都只能匹配到后表的一行结果. 并且查询的比较操作通常是 =, 查询效率较高. 例如:

mysql> EXPLAIN SELECT * FROM customer, order_info WHERE customer.id = order_info.user_id\G*************************** 1. row ***************************  id: 1 select_type: SIMPLE table: order_info partitions: NULL  type: indexpossible_keys: user_product_detail_index  key: user_product_detail_index key_len: 314  ref: NULL  rows: 9 filtered: 100.00 Extra: Using where; Using index*************************** 2. row ***************************  id: 1 select_type: SIMPLE table: customer partitions: NULL  type: eq_refpossible_keys: PRIMARY  key: PRIMARY key_len: 8  ref: test.order_info.user_id  rows: 1 filtered: 100.00 Extra: NULL2 rows in set, 1 warning (0.00 sec)

ref: 此类型通常出现在多表的 join 查询, 针对于非唯一或非主键索引, 或者是使用了 最左前缀 规则索引的查询.

例如下面这个例子中, 就使用到了 ref 类型的查询:

mysql> EXPLAIN SELECT * FROM customer, order_info WHERE customer.id = order_info.user_id AND order_info.user_id = 5\G*************************** 1. row ***************************  id: 1 select_type: SIMPLE table: customer partitions: NULL  type: constpossible_keys: PRIMARY  key: PRIMARY key_len: 8  ref: const  rows: 1 filtered: 100.00 Extra: NULL*************************** 2. row ***************************  id: 1 select_type: SIMPLE table: order_info partitions: NULL  type: refpossible_keys: user_product_detail_index  key: user_product_detail_index key_len: 9  ref: const  rows: 1 filtered: 100.00 Extra: Using index2 rows in set, 1 warning (0.01 sec)

range: 表示使用索引范围查询, 通过索引字段范围获取表中部分数据记录. 这个类型通常出现在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中.当 type 是 range 时, 那么 EXPLAIN 输出的 ref 字段为 NULL, 并且 key_len 字段是此次查询中使用到的索引的最长的那个.

例如下面的例子就是一个范围查询:

mysql> EXPLAIN SELECT * FROM customer WHERE id BETWEEN 2 AND 8 \G*************************** 1. row ***************************  id: 1 select_type: SIMPLE table: customer partitions: NULL  type: rangepossible_keys: PRIMARY  key: PRIMARY key_len: 8  ref: NULL  rows: 7 filtered: 100.00 Extra: Using where1 row in set, 1 warning (0.00 sec)

index: 表示全索引扫描(full index scan), 和 ALL 类型类似, 只不过 ALL 类型是全表扫描, 而 index 类型则仅仅扫描所有的索引, 而不扫描数据.

index 类型通常出现在: 所要查询的数据直接在索引树中就可以获取到, 而不需要扫描数据. 当是这种情况时, Extra 字段 会显示 Using index.

例如:

mysql> EXPLAIN SELECT name FROM customer \G*************************** 1. row ***************************  id: 1 select_type: SIMPLE table: customer partitions: NULL  type: indexpossible_keys: NULL  key: name_index key_len: 152  ref: NULL  rows: 10 filtered: 100.00 Extra: Using index1 row in set, 1 warning (0.00 sec)

上面的例子中, 我们查询的 name 字段恰好是一个索引, 因此我们直接从索引中获取数据就可以满足查询的需求了, 而不需要查询表中的数据. 因此这样的情况下, type 的值是 index, 并且 Extra 的值是 Using index.

  • ALL: 表示全表扫描, 这个类型的查询是性能最差的查询之一. 通常来说, 我们的查询不应该出现 ALL 类型的查询, 因为这样的查询在数据量大的情况下, 对数据库的性能是巨大的灾难. 如一个查询是 ALL 类型查询, 那么一般来说可以对相应的字段添加索引来避免.

下面是一个全表扫描的例子, 可以看到, 在全表扫描时, possible_keys 和 key 字段都是 NULL, 表示没有使用到索引, 并且 rows 十分巨大, 因此整个查询效率是十分低下的.

mysql> EXPLAIN SELECT age FROM customer WHERE age = 20 \G*************************** 1. row ***************************  id: 1 select_type: SIMPLE table: customer partitions: NULL  type: ALLpossible_keys: NULL  key: NULL key_len: NULL  ref: NULL  rows: 10 filtered: 10.00 Extra: Using where1 row in set, 1 warning (0.00 sec)

type 类型的性能比较

通常来说, 不同的 type 类型的性能关系如下:

ALL < index < range ~ index_merge < ref < eq_ref < const < system

ALL 类型因为是全表扫描, 因此在相同的查询条件下, 它是速度最慢的.

而 index 类型的查询虽然不是全表扫描, 但是它扫描了所有的索引, 因此比 ALL 类型的稍快.后面的几种类型都是利用了索引来查询数据, 因此可以过滤部分或大部分数据, 因此查询效率就比较高了.

对程序员来说,若保证查询至少达到range级别或者最好能达到ref则算是一个优秀而又负责的程序员。

  • ALL:(full table scan)全表扫描无疑是最差,若是百万千万级数据量,全表扫描会非常慢。

  • index:(full index scan)全索引文件扫描比all好很多,毕竟从索引树中找数据,比从全表中找数据要快。

  • range:只检索给定范围的行,使用索引来匹配行。范围缩小了,当然比全表扫描和全索引文件扫描要快。sql语句中一般会有between,in,>,< 等查询。

  • ref:非唯一性索引扫描,本质上也是一种索引访问,返回所有匹配某个单独值的行。比如查询公司所有属于研发团队的同事,匹配的结果是多个并非唯一值。

  • eq_ref:唯一性索引扫描,对于每个索引键,表中有一条记录与之匹配。比如查询公司的CEO,匹配的结果只可能是一条记录,

  • const:表示通过索引一次就可以找到,const用于比较primary key 或者unique索引。因为只匹配一行数据,所以很快,若将主键至于where列表中,MySQL就能将该查询转换为一个常量。

  • system:表只有一条记录(等于系统表),这是const类型的特列,平时不会出现,了解即可

possible_key

spossible_keys 表示 MySQL 在查询时, 能够使用到的索引. 注意, 即使有些索引在 possible_keys 中出现, 但是并不表示此索引会真正地被 MySQL 使用到. MySQL 在查询时具体使用了哪些索引, 由 key 字段决定.

key

此字段是 MySQL 在当前查询时所真正使用到的索引.

key_len

表示查询优化器使用了索引的字节数. 这个字段可以评估组合索引是否完全被使用, 或只有最左部分字段被使用到.

key_len 的计算规则如下:

  • 字符串

    • char(n): n 字节长度

    • varchar(n): 如果是 utf8 编码, 则是 3
      n + 2字节; 如果是 utf8mb4 编码, 则是 4
      n + 2 字节.

  • 数值类型:

    • TINYINT: 1字节

    • SMALLINT: 2字节

    • MEDIUMINT: 3字节

    • INT: 4字节

    • BIGINT: 8字节

  • 时间类型

    • DATE: 3字节

    • TIMESTAMP: 4字节

    • DATETIME: 8字节

  • 字段属性: NULL 属性 占用一个字节. 如果一个字段是 NOT NULL 的, 则没有此属性.

我们来举两个简单的栗子:

mysql> EXPLAIN SELECT * FROM order_info WHERE user_id < 3 AND product_name = 'p1' AND productor = 'WHH' \G*************************** 1. row ***************************   id: 1 select_type: SIMPLE  table: order_info partitions: NULL   type: rangepossible_keys: user_product_detail_index   key: user_product_detail_index  key_len: 9   ref: NULL   rows: 5  filtered: 11.11  Extra: Using where; Using index1 row in set, 1 warning (0.00 sec)

上面的例子是从表 order_info 中查询指定的内容, 而我们从此表的建表语句中可以知道, 表 order_info 有一个联合索引:

KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)

不过此查询语句 WHERE user_id < 3 AND product_name = 'p1' AND productor = 'WHH' 中, 因为先进行 user_id 的范围查询, 而根据 最左前缀匹配 原则, 当遇到范围查询时, 就停止索引的匹配, 因此实际上我们使用到的索引的字段只有 user_id, 因此在 EXPLAIN 中, 显示的 key_len 为 9. 因为 user_id 字段是 BIGINT, 占用 8 字节, 而 NULL 属性占用一个字节, 因此总共是 9 个字节. 若我们将user_id 字段改为 BIGINT(20) NOT NULL DEFAULT '0', 则 key_length 应该是8.

上面因为 最左前缀匹配 原则, 我们的查询仅仅使用到了联合索引的 user_id 字段, 因此效率不算高.

接下来我们来看一下下一个例子:

mysql> EXPLAIN SELECT * FROM order_info WHERE user_id = 1 AND product_name = 'p1' \G;*************************** 1. row ***************************   id: 1 select_type: SIMPLE  table: order_info partitions: NULL   type: refpossible_keys: user_product_detail_index   key: user_product_detail_index  key_len: 161   ref: const,const   rows: 2  filtered: 100.00  Extra: Using index1 row in set, 1 warning (0.00 sec)

这次的查询中, 我们没有使用到范围查询, key_len 的值为 161. 为什么呢? 因为我们的查询条件 WHERE user_id = 1 AND product_name = 'p1' 中, 仅仅使用到了联合索引中的前两个字段, 因此 keyLen(user_id) + keyLen(product_name) = 9 + 50 * 3 + 2 = 161

rows

rows 也是一个重要的字段. MySQL 查询优化器根据统计信息, 估算 SQL 要查找到结果集需要扫描读取的数据行数.

这个值非常直观显示 SQL 的效率好坏, 原则上 rows 越少越好.

Extra

EXplain 中的很多额外的信息会在 Extra 字段显示, 常见的有以下几种内容:

  • Using filesort

当 Extra 中有 Using filesort 时, 表示 MySQL 需额外的排序操作, 不能通过索引顺序达到排序效果. 一般有 Using filesort, 都建议优化去掉, 因为这样的查询 CPU 资源消耗大.

例如下面的例子:

mysql> EXPLAIN SELECT * FROM order_info WHERE user_id = 1 AND product_name = 'p1' \G;*************************** 1. row ***************************   id: 1 select_type: SIMPLE  table: order_info partitions: NULL   type: refpossible_keys: user_product_detail_index   key: user_product_detail_index  key_len: 161   ref: const,const   rows: 2  filtered: 100.00  Extra: Using index1 row in set, 1 warning (0.00 sec)

我们的索引是

KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)

但是上面的查询中根据 product_name 来排序, 因此不能使用索引进行优化, 进而会产生 Using filesort.

如果我们将排序依据改为 ORDER BY user_id, product_name, 那么就不会出现 Using filesort 了. 例如:

mysql> EXPLAIN SELECT * FROM order_info ORDER BY user_id, product_name \G*************************** 1. row ***************************   id: 1 select_type: SIMPLE  table: order_info partitions: NULL   type: indexpossible_keys: NULL   key: user_product_detail_index  key_len: 253   ref: NULL   rows: 9  filtered: 100.00  Extra: Using index1 row in set, 1 warning (0.00 sec)
  • Using index

"覆盖索引扫描", 表示查询在索引树中就可查找所需数据, 不用扫描表数据文件, 往往说明性能不错

  • Using temporary

查询有使用临时表, 一般出现于排序, 分组和多表 join 的情况, 查询效率不高, 建议优化.

感谢各位的阅读!关于"MySQL查询优化的简介"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

查询 索引 类型 数据 字段 字节 结果 范围 例子 性能 效率 语句 面的 重要 内容 情况 排序 输出 信息 只有 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 百单网互联网科技有限公司 喔噻互联网科技有限公司 广东万易互联网科技有限公司惠州分公司 中关村互联网 科技集团 互联网科技工作委员会毛军 科技和互联网公司 科技与互联网 互联网公司 科技公司 善班互联网科技有限公司 西藏七维空间互联网科技有限公司 天速互联网络科技有限公司 互联网经济与金融科技 互联网与医疗科技 华夏恒生科技与互联网科技 长沙茂晟互联网科技有限公司 科技赋能视角下互联网保险发展 互联网时代科技型公司的机会 科技互联网经济论坛 互联网科技和医药行业 广东猫王互联网科技有限公司电话 广州百单互联网科技有限公司 互联网是不是新科技 互联网科技金融 猪兼强互联网科技 银川新华互联网科技学校地址 现代科技和互联网有关系吗 河南猎农互联网科技有限公司 移动互联网科技公司 科技互联网企业在行动 六子台科技互联网展会
0