使用 order by 时,Mysql 查询运行非常慢
Posted
技术标签:
【中文标题】使用 order by 时,Mysql 查询运行非常慢【英文标题】:Mysql query runs very slow when using order by 【发布时间】:2011-12-27 18:20:20 【问题描述】:在订购时,以下查询需要 30 秒才能完成。如果没有命令,它会在 0.0035 秒内完成。我已经在字段“名称”上有一个索引。字段“id”是主键。我在这张表中有 400,000 条记录。请帮忙,使用order by时查询有什么问题。
SELECT *
FROM users
WHERE name IS NOT NULL
AND name != ''
AND ( status IS NULL OR status = '0' )
order by id desc
limit 50
更新:(最后的解决方案) 大家好,感谢您的帮助。以下是您要求的一些更新:
下面是解释的输出。
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE 用户范围名称 name 258 NULL 226009 使用 where;使用文件排序
是的,此表中大约有 20 个字段。
以下是我拥有的索引:
键名类型基数字段
初级初级 418099 id 名称索引 411049 名称
解决方案: 事实证明,具有空值的字段是原因。将 where 条件中的这 2 个字段设为 NOT NULL 时,只需 0.000x 秒。但奇怪的是,如果我创建 (status,name,id DESC) 或 (status,name,id) 的索引,它会增加到 29 秒。
【问题讨论】:
至少,您应该在查询中包含“解释”的输出。 明确标识要查询的列时是否有任何变化? 您的结果集是否庞大,您的id
是否已编入索引?
order by id(无 desc)的表现如何?
@Paul Sanwald - 我添加了解释的输出。
【参考方案1】:
你绝对应该有复合索引。一个包含您作为 DBMS 所需的所有字段的单个索引实际上不能在单个查询中使用多个索引。
OR 子句对索引并不友好,所以如果可以的话,我建议将status
设置为NOT NULL。我假设 NULL 与零号没有任何不同的含义。这对实际使用索引有很大帮助。
不知道name != ''
优化了多少。语义上相等的将是name > ''
(意味着它在字母表中的后面),这可能还会为您节省一些 CPU 周期。
然后您必须决定列的显示顺序。经验法则可能是基数,即字段可能具有的值。
通过这个:
ALTER TABLE users ADD INDEX order1 (status, name, id DESC);
编辑
您不需要删除索引。 mysql 会很快选择最好的一个而忽略其余的。它们仅在UPDATE
s 上花费磁盘空间和一些 CPU 周期。但是,如果您在任何情况下都不需要它们,当然可以删除它们。
时间长是因为访问您的表很慢。这可能是由动态长度字段(如 TEXT 或 BLOB)引起的。如果您并不总是需要这些,您可以将它们移动到一个双辅助表中,例如:
users (id, name, status, group_id)
profile (user_id, birthdate, gender, motto, cv)
通过这种方式,基本的系统操作可以通过有关用户的受限信息来完成,并且所有其他真正与用户相关的内容内容只有在真正与用户相关联时才需要使用需要。
编辑2
你通过指定它(或更多)来提示 MySQL 使用哪个索引:
SELECT id, name FROM users USE INDEX (order1) WHERE name != '' and status = '0' ORDER BY id DESC
【讨论】:
如你所说,我创建了一个索引“ALTER TABLE users ADD INDEX namstatid (name, status, id DESC);”。现在,如果我只选择这 3 列(名称、状态、id)中的字段,则需要 0.8 秒。但如果我使用除此之外的任何列,则大约需要 26 秒。另外,我是否应该删除其他索引(目前我有 2 个索引。一个处于状态,另一个处于名称)。 当我使用“解释选择 id,someotherfield from users where name IS NOT NULL AND name != '' AND (status IS NULL OR status = '0') order by id desc limit 50 " 似乎没有使用索引。那么,即使我们移动数据,它显然也不会使用索引?在这种情况下,问题将持续存在。将数据移动到子表中;涉及修改各种其他相关联的东西。我想确保这会起作用。它唯一使用索引的时间是,如果我只使用索引中的字段。请告诉我你的想法。非常感谢! @3iguru 正如我在帖子中所说:OR
表示可能的结果不能限制在索引内的连续区域。您将有两个不同的区域:一个用于NULL
,一个用于0
。 - 这样 MySQL 不能充分利用索引。 - 将字段更改为 NOT NULL
可能会有所帮助。
抱歉,我已经删除了那些 OR 部分,并且只使用以下查询:解释 SELECT id,name FROM users WHERE name != '' and status = '0' ORDER BY id DESC LIMIT 30 。输出是“额外:使用 where;使用索引;使用 filesort Ref:null”。当我包含不属于索引的任何其他字段时,它会输出“Extra:Using where;Using filesort Ref:null”。这意味着当我使用任何附加字段时,它不使用索引。我还通过使字段不为空来进行测试。请告诉我你的想法。
@3iguru 我编辑了我的答案,还请注意使用WHERE name > ''
而不是!=
的可能性还请注意,我推荐:(status, name, id DESC)
而不是(name,status,id)
【参考方案2】:
没有解释很难说,但很可能你还需要一个索引 “状态”列。单个表查询的缓慢几乎总是归结为执行全表扫描而不是使用索引的查询。
尝试做:
explain SELECT *
FROM users
WHERE name IS NOT NULL
AND name != ''
AND ( status IS NULL OR status = '0' )
order by id desc
limit 50
并发布输出。您可能会看到它正在执行全表扫描,因为它没有状态索引。 here's some documentation on using "explain"。如果您想了解更多背景信息,请this is a nice article 了解您遇到的问题。
【讨论】:
嗨,下面是解释的输出。下面是解释的输出。 id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE users range name name 258 NULL 226009 using where;使用文件排序以上是关于使用 order by 时,Mysql 查询运行非常慢的主要内容,如果未能解决你的问题,请参考以下文章
mysql使用带有子查询的临时表,但不是group by和order by
加速使用 Group By 和 Order By 的多表 Mysql 查询