执行计划理解
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(或者) 运算,要求所有参与运算的字段都存在索引,才会使用到索引。
![post-qrcode](https://www.bomx.cn/wp-content/uploads/2023/05/Snipaste_2023-05-15_23-51-49.jpg)