使用 MySQL
平时编写 SQL 的时候,可以使用 Explain
关键字,根据执行结果进行优化 SQL
。
如下图通过 Explain
查看 SQL 执行结果
根据EXPLAIN
结果,我们可以看到以下信息:
id
: 查询的标识符,这里是1,对于简单的单表查询,通常只有一个。select_type
: 查询类型,这里是SIMPLE
,表明这是一个不包含子查询或UNION的简单查询。table
: 涉及的表名,即user_login_log
。partitions
: 表的分区信息,这里为NULL
,意味着表没有使用分区。type
: 访问类型,这里是ALL
,表示进行了全表扫描,这是效率较低的一种访问方式,意味着没有使用到索引。possible_keys
: 可能用到的索引,这里是NULL
,表示没有合适的索引可供优化器选择。key
: 实际使用的索引,同样为NULL
,确认了没有使用索引。key_len
: 使用的索引长度,因为没有使用索引,所以也是NULL
。ref
: 与索引比较的列或常量,由于未使用索引,此列为NULL
。rows
: 预计扫描的行数,这里是293706行,表明MySQL预计需要扫描整个表来完成这个查询。filtered
: 表示存储引擎返回的数据在server层过滤后,能用到的比例,这里是100%,意味着所有扫描的行都会返回给客户端(在LIMIT
限制之前)。Extra
: 额外的信息,显示为Using filesort
,意味着MySQL需要对结果进行排序,因为没有索引可以直接提供有序的数据,这对于大表和频繁执行的排序查询来说,性能开销较大。
综上所述,当前这个查询没有利用到任何索引,执行时进行了全表扫描并进行了文件排序来完成ORDER BY
操作。为了优化这个查询,考虑在login_date
字段上创建一个索引,如果经常需要这样的查询并且数据量大,创建一个包含login_date
和login_ip
的复合索引可能会更有效,这样不仅可以加速排序,还能覆盖查询避免访问表数据。
根据您的EXPLAIN
输出信息,情况有了变化:
id
: 依然是1,代表这是一个简单的查询。select_type
: 依然是SIMPLE
。table
: 依然是user_login_log
表。type
: 现在是index
,这意味着查询使用了索引进行扫描,但不是通过索引来直接定位记录(这与ALL
不同,也不同于通过索引直接找到记录的const
或eq_ref
)。possible_keys
: 显示可能使用的索引是idx_user_id
,但实际上使用了另一个索引。key
: 实际使用的索引是idx_login_date
,这与您的问题描述不符,看起来可能是输入时的笔误或是理解上的误会,因为通常EXPLAIN
结果中的key
列应直接对应于实际采用的索引名。key_len
: 4字节,这通常对应于索引字段的大小,对于日期时间类型(如DATETIME或TIMESTAMP),这符合预期。rows
: 预计扫描的行数是13行,相比之前的全表扫描大大减少,说明索引正在起作用缩小扫描范围。filtered
: 7.16%,表明在索引扫描过程中,预计有7.16%的行是需要的,这个值反映了通过索引筛选后的数据相关性。Extra
:Using where
,表明虽然使用了索引进行扫描,但是在索引中无法直接找到满足条件的所有列,所以需要回表或者是在已经通过索引筛选出的行中进一步应用WHERE条件过滤。
基于以上信息,看起来查询优化器选择了idx_login_date
索引来帮助执行查询,而不是之前讨论的针对特定login_ip
的选择。如果您的实际意图是查看关于如何优化原始SQL(即按login_date
排序并限制结果数量的查询)的索引使用情况,之前的建议仍然适用:确保login_date
上有索引,并考虑是否需要创建一个包含login_date
和login_ip
的复合索引来进一步优化排序和查询速度。