mysql 5.7 通过执行计划 查看 组合索引 最左前缀原则

作者: JONE 分类: 编程 发布时间: 2021-04-23 20:34

执行计划理解

mysql explain 可以得到mysql语句的执行过程信息,可应用于数据的增删改查的执行过程分析,insert,delete,update,replace,selectexplain的输出列id: select_id,该SELECT标识符

select_type: SELECT查询的类型

table: table_name,表示查询涉及的表或衍生表

partitions: 匹配的分区

type: access_type 连接类型

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

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

key_len: key_length 所选键的长度,表示查询优化器使用了索引的字节数. 这个字段可以评估组合索引是否完全被使用, 或只有最左部分字段被使用到.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 的, 则没有此属性.

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

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

filtered: 这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数

Extra: 没有 附加信息

select_type列的值如下:simple:表示是简单的select,不设计到联表或子查询primary:表示此查询是最外层的查询union:表示此查询是 UNION 的第二或随后的查询dependent union:UNION 中的第二个或后面的查询语句, 取决于外面的查询union result:UNION 的结果subquery:子查询中的第一个 SELECTdependent subquery:子查询中的第一个 SELECT, 取决于外面的查询. 即子查询依赖于外层查询的结果derived:派生表materialized:物化子查询uncacheable subquery:无法缓存结果的子查询uncacheable union:无法缓存结果的联表

join typetype 字段比较重要, 它提供了判断查询是否高效的重要依据依据. 通过 type 字段, 我们判断此次查询是 全表扫描 还是 索引扫描 等.查询速度 system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>all

1、system:该表只有一行(=系统表)。这是const连接类型的特例2、const: 针对主键或唯一索引的等值查询扫描, 最多只返回一行数据. const 查询速度非常快, 因为它仅仅读取一次即可.例如下面的这个查询, 它使用了主键索引, 因此 type 就是 const 类型的。explain (select * from ci_film_resource where id = 1)3、eq_ref:此类型通常出现在多表的 join 查询, 表示对于前表的每一个结果, 都只能匹配到后表的一行结果. 并且查询的比较操作通常是 =, 查询效率较高explain (select * from ci_film,ci_film_resource where ci_film.film_id =ci_film_resource.film_id );4、ref:此类型通常出现在多表的 join 查询, 针对于非唯一或非主键索引, 或者是使用了 最左前缀 规则索引的查询5、fulltext:使用fulltext全文索引进行执行连接6、ref_or_null:与ref方法类似,只是增加了null值的比较。实际用的不多7、index_merge:表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取多个索引,性能可能大部分时间都不如range8、unique_subquery:用于where中的in形式子查询,子查询返回不重复值唯一值9、index_subquery:用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重10、range:表示使用索引范围查询, 通过索引字段范围获取表中部分数据记录. 这个类型通常出现在 =, <>, >, >=, , BETWEEN, IN() 操作中;当 type 是 range 时, 那么 EXPLAIN 输出的 ref 字段为 NULL, 并且 key_len 字段是此次查询中使用到的索引的最长的那个11、index:表示全索引扫描(full index scan), 和 ALL 类型类似, 只不过 ALL 类型是全表扫描, 而 index 类型则仅仅扫描所有的索引, 而不扫描数据; 类型通常出现在: 所要查询的数据直接在索引树中就可以获取到, 而不需要扫描数据. 当是这种情况时, Extra 字段 会显示 Using index12、all:表示全表扫描, 这个类型的查询是性能最差的查询之一;通常来说, 我们的查询不应该出现 ALL 类型的查询, 因为这样的查询在数据量大的情况下, 对数据库的性能是巨大的灾难; 如一个查询是 ALL 类型查询, 那么一般来说可以对相应的字段添加索引来避免

用于测试的数据表:ci_film

CREATE TABLE `ci_film` (`film_id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘影片ID’, `film_name` varchar(255) NOT NULL COMMENT ‘影片名称’, `film_alias_name` varchar(255) NOT NULL COMMENT ‘影片别名’, `film_tags` varchar(100) NOT NULL COMMENT ‘影片标签’, `film_img` varchar(255) NOT NULL DEFAULT ”, `film_intro` text NOT NULL COMMENT ‘影片介绍’, `type_id` int(11) NOT NULL COMMENT ‘影片类型’, `release_time` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘影片上映时间’, `week_up_day` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘每周更新日期’, `total_episode` int(11) NOT NULL DEFAULT ‘1’ COMMENT ‘影片集数’, `film_status` tinyint(1) NOT NULL DEFAULT ‘1’ COMMENT ‘0=删除,1=正常,2=连载,3=完结,4=未开播’, `film_starring` varchar(255) DEFAULT ” COMMENT ‘影片主演’, `area_id` int(11) NOT NULL COMMENT ‘影片地区ID’, `film_score` decimal(2,2) NOT NULL DEFAULT ‘0.00’ COMMENT ‘影片评分’, `now_episode` smallint(5) NOT NULL DEFAULT ‘0’ COMMENT ‘更新到第几集’, `film_poster` varchar(45) NOT NULL DEFAULT ‘0’ COMMENT ‘影片海报’, `create_time` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘创建时间’, `update_time` int(11) NOT NULL DEFAULT ‘0’, `film_sub` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘订阅数’, `film_watched` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘影片查看数’, `comment_num` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘影片评论数’, PRIMARY KEY (`film_id`), UNIQUE KEY `film_name_UNIQUE` (`film_name`), KEY `film_alias_name` (`film_alias_name`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT=’影片列表’;

ci_film_episode

CREATE TABLE `ci_film_episode` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT, `resource_id` int(11) unsigned NOT NULL COMMENT ‘资源ID’, `film_id` int(11) unsigned NOT NULL COMMENT ‘影片ID’, `episode_num` int(11) unsigned NOT NULL DEFAULT ‘1’ COMMENT ‘集数’, `episode_name` varchar(255) NOT NULL DEFAULT ” COMMENT ‘集数标题’, `web_id` int(11) unsigned NOT NULL COMMENT ‘网站ID’, `film_uri` varchar(255) NOT NULL DEFAULT ” COMMENT ‘影片地址’, `video_uri` varchar(255) NOT NULL DEFAULT ” COMMENT ‘影片video地址’, `video_status` tinyint(1) unsigned NOT NULL DEFAULT ‘1’ COMMENT ‘0=删除,1=正常’, `create_time` int(11) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘创建时间’, `update_time` int(10) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘更新时间’, PRIMARY KEY (`id`), UNIQUE KEY `id_UNIQUE` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT=’影片集数’;

ci_film_resource

CREATE TABLE `ci_film_resource` (`id` int(11) NOT NULL AUTO_INCREMENT, `film_id` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘影片ID’, `film_name` varchar(255) NOT NULL DEFAULT ” COMMENT ‘影片名字’, `nick_name` varchar(255) NOT NULL DEFAULT ” COMMENT ‘影片别名’, `film_starring` varchar(255) DEFAULT NULL, `release_time` int(11) NOT NULL DEFAULT ‘0’, `film_img` varchar(255) NOT NULL DEFAULT ” COMMENT ‘影片图片’, `film_intro` varchar(800) NOT NULL DEFAULT ” COMMENT ‘影片描述’, `film_url` varchar(255) NOT NULL COMMENT ‘影片地址’, `web_id` int(11) NOT NULL COMMENT ‘网址ID’, `total_episode` int(11) NOT NULL DEFAULT ‘0’, `week_up_day` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘每周更新日期’, `week_up_info` varchar(255) NOT NULL DEFAULT ”, `now_episode` int(11) NOT NULL DEFAULT ‘0’, `area_id` int(11) NOT NULL, `type_id` int(11) NOT NULL, `resource_desc` varchar(255) NOT NULL DEFAULT ”, `film_status` tinyint(1) NOT NULL DEFAULT ‘1’, `create_time` int(11) NOT NULL, `update_time` int(11) NOT NULL DEFAULT ‘0’, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT=’影片资源’;

例子:#查全表 explain(select * from ci_film); /* id:1 select_type:SIMPLE table:ci_film type:ALL rows:579 filtered:100.00 */ #查全表有唯一索引字段 explain(select film_name from ci_film); /* id:1 select_type:SIMPLE table:ci_film type:index possible_keys:NULL keys:film_name_UNIQUE key_len:767 rows:579 filtered:100.00 Extra:Using index */ #查全表有索引字段 explain(select film_alias_name from ci_film); /* id:1 select_type:SIMPLE table:ci_film type:index keys:film_alias_name key_len:767 rows:579 filtered:100.00 Extra:Using index */ #查全表没有索引的字段 explain(select film_intro from ci_film); /* id:1 select_type:SIMPLE table:ci_film type:ALL rows:579 filtered:100.00 Extra:NULL */

explain(select * from ci_film where film_id = 0)id:1 slect_type:SIMPLE Extra:no matching row in const table

explain(select * from ci_film where film_id = 10)/* id:1 select_type:SIMPLE table:ci_film type:const possible_keys:PRIMARY key:PRIMARY key_len:4 ref:const row:1 filtered:100.00 */ explain(select * from ci_film where film_id >10); /* id:1 select_type:SIMPLE table:ci_film type:range possible_keys:PRIMARY key:PRIMARY key_len:4 rows:289 filtered:100.00 Extra:Using where */

explain(select * from ci_film where film_id <10);explain(select * from ci_film where film_id !=10);

联表查询:explain(select * from ci_film,ci_film_resource where ci_film.film_id != ci_film_resource.film_id); /* id:1 slect_type:SIMPLE table:ci_film type:ALL rows:579 filtered:100.00 id:1 slect_type:SIMPLE table:ci_film_resource type:ALL rows:597 filterd:90.00 Extra:Using where; Using join buffer (Block Nested Loop)/使用where;使用了连接缓存(块嵌套循环) */

explain(select * from ci_film,ci_film_resource where ci_film.film_id = ci_film_resource.film_id ) ;/*id:1 slect_type:SIMPLE table:ci_film_resource type:ALL rows:597 filtered:100.00id:1 slect_type:SIMPLE table:ci_film type:eq_ref possible_keys:PRIMARY key:PRIMARY key_len:4 ref:cinema.ci_film_resource.film_id rows:1 filtered:100.00*/SHOW WARNINGS显示由上一个生成消息的语句导致的错误、警告和注意消息

Select tables optimized away:选择优化的表格在没有使用group by 子句的情况下,基于索引优化min/max操作,或者对于myisam存储引擎优化count(*)操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即可完成优化tips:count(*)在myisam和innodb中表现不一样,myisam是直接读取表中标识,而innodb是遍历表

Using where:使用了where过滤

Using join buffer:使用了连接缓存

Impossible WHERE:

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

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

Using temporary:查询有使用临时表, 一般出现于排序, 分组和多表 join 的情况, 查询效率不高, 建议优化
————————————————
原文链接:https://blog.csdn.net/weixin_31629313/article/details/113159176

组合索引最左前缀原则总结

设:组合索引 A、B、C、D   4列索引

where 语句中 A B C D  的条件顺序不一定非得依次固定,但是想想要此组合索引生效,那么必须有 A

// 示例如下

通过上图可得,必须最左 type 字段存在,才能调用 type_status_views_creatat  这个组合索引,至于条件字段的顺序则不影响

另外使用组合索引时避免使用 or 条件语句查询,or 条件语句会使组合索引失效。

如果出现OR(或者)运算,要求所有参与运算的字段都存在索引,才会使用到索引。