简介
explain显示了mysql如何用索引来处理DML语句和连接表,explain显示的信息有助于选择更好的索引并写出更优化的查询句。
explain列解释
- table:显示这一行的数据是关于哪个表的
- type:从最佳到最差的连接类型顺序显示了连接的类型:const,eq_reg,ref,range, index ,ALL
- possible_keys:如果显示可能应用于此表中的索引是空的,则不可能的索引可能是相关域从WHERE在句子中选择合适的句子。
- key:实际使用的索引。NULL,没有索引。mysql会选择优化不足的索引。在这种情况下,可以select语句中使用USE INDEX(indexName)强制使用索引或使用索引IGNORE INDEX(indexName)来强制mysql忽略索引。
- key_len:使用的索引长度。长度越短越好,不损失精度。
- ref:如果可能的话,使用显示索引的哪一列是常数。
- rows:mysql返回请求数据的行数认为必须检查。
- Extra:关于mysql如何分析查询的额外信息,坏例子是Using temporary 和 Using filesort,意思mysql索引根本不能用,结果是检索会很慢。
Extra列回的描述意义
- Distinct:一旦mysql找到与之匹配的行,就不再搜索了
- Not exists:mysql优化了LEFTJOIN ,一旦找到匹配LEFT JOIN不再搜索标准行。
- Range checked for each Record(index map:#):没有理想的索引,所以对于前表中的每一行组合,mysql检查时使用索引,并使用它从表中返回。这是使用索引的最慢连接之一。
- Using filesort:查询需要优化才能看到这一点。mysql需要额外的步骤来找出如何对返回行进行排序。它根据连接类型、存储排序键和匹配条件的所有行指针对所有行进行排序。
- Using index:列数据是从只使用索引中的信息而不读取实际行动的表返回的,这发生在对表的所有请求列都是同一索引的部分。
- Using temporary:查询需要优化才能看到这一点。在这里,mysql通常在不同的列集中创建临时表来存储结果ORDER BY上,而不是GROUP BY上。
- Where used 使用了WHERE从句限制哪些行将与下一个表匹配或返回给用户。如果您不想返回表中的所有行类型ALL或index,这将发生,或查询不同连接类型的问题解释(按效率高低顺序排序)
- system只有一行表:system表,这是const特殊情况的连接类型
- const:表中记录的最大值可以与查询相匹配(索引可以是主键或唯一索引)。因为只有一行,这个值实际上是常数,因为mysql先读这个值,再把它当常数。
- eq_ref:在连接中,mysql查询时,从前表中读取每个记录的组合,查询时使用索引作为主键或唯一键。
- ref:这种链接类型只发生在查询使用不是唯一或主键的按钮或这些类型的部分(例如,使用最左前缀)时。所有的记录都将从表中读取。这种类型严重取决于根据索引匹配的记录。
- range:这种连接类型使用索引返回到一个范围内,例如使用>或<发生在寻找东西的时候。
- index:这种连接类型完全扫描了前表中的每个记录(比ALL更好,因为索引通常小于表数据)
- ALL:这种连接类型完全扫描了前面的每个记录,这通常很糟糕,应该尽量避免。
案例
优化器中range使用案例:
在寻求优化时,需要了解三点:
1、表的DDL
2、SQL语句
3、EXPLIAN
CREATE TABLE `account` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 用户资金账户, `user_id` bigint(20) DEFAULT NULL COMMENT '用户ID', `amount` decimal(10,2) DEFAULT '0.00' COMMENT '总金额', `available` decimal(10,2) DEFAULT '0.00' COMMENT 可用余额, `presented` decimal(10,2) DEFAULT '0.00' COMMENT 活动赠送金额, `deposit_amount` decimal(10,2) DEFAULT '0.00' COMMENT 存款金额, `freeze_amount` decimal(10,2) DEFAULT NULL COMMENT 冻结金额, `status` tinyint(4) DEFAULT NULL COMMENT '账户状态', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT 创造时间, `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (`id`), UNIQUE KEY `uid_index` (`user_id`) USING BTREE, KEY `create_time` (`create_time`) ) ENGINE=InnoDB AUTO_INCREMENT=6184535 DEFAULT CHARSET=utf8
EXPLAIN SELECT * FROM account WHERE id>1
id select_type table typepossible_keyskey key_lenref rowsExtra 1 SIMPLE account rangePRIMARY PRIMARY 8 2808936Using where
好像走了RANGE…
EXPLAIN SELECT * FROM account WHERE user_id>1
id select_typetabletypepossible_keyskeykey_lenref rows Extra 1 SIMPLE accountALL uid_index 5617872 Using where
这里出了问题…
EXPLAIN SELECT * FROM account WHERE user_id>1 AND user_id<2222
id select_typetable typepossible_keyskey key_lenref rows Extra 1 SIMPLE account range uid_index uid_index 9 2218Using index condition
##第一个好像走了。range,实际是因为primary key 决定性因素
第二个有索引但全表扫描,只有开区间没有闭区间
在第3个包含了开区间和闭区间直接走了range
由此:在SQL优化时,如果要使用,range要实现索引,必须满足开始点和结束点,俗称开闭区间。
例如,在时间字段上添加二级索引,如:
SELECT * FROM account WHERE create_time>DATE_FORMAT(2017-01-26)Y-%m-%d’) AND create_time<DATE_FORMAT(2017-01-29)Y-%m-%d’)
SELECT * FROM account WHERE create_time>DATE(“2017-01-26”)AND create_time <DATE(“2017-01-29”)
SELECT * FROM account WHERE create_time>STR_TO_DATE(2017-01-26)Y-%m-%d’)AND create_time <STR_TO_DATE(2017-01-29)Y-%m-%d’)
id select_typetabletypepossible_keyskey key_lenrefrowsExtra 1 SIMPLE accountrangecreate_time create_time6 129610Using index condition
一般WHERE在不接近的情况下,包含函数的前提是不能索引WHERE时间生效