使用索引时MySQL查询变慢

时间:2019-10-08 20:32:05

标签: mysql query-optimization

我到了一个要点,就是当我在where子句中使用索引时,为什么下面的MySQL查询为什么变慢了,我到了一个地步。使我发疯的列称为已删除。该表包含480万行。

查询:

SELECT SQL_NO_CACHE SUM(amount)/100 FROM transactions WHERE (type="Payment" or type="Refund") and deleted is NULL

当该列为索引时,该查询花费略高于11秒的时间;而当该列未被索引时或当我使用USE INDEX()时,该查询则花费3秒,这告诉优化器不要使用任何索引。

MySQL 5.6版,已在AWS Aurora db.r5.xlarge(4CPU / 32GB)中进行了测试

表结构:

id int(11) NOT NULL, type enum('Charge','Payment','Refund','Credit Adjustment','Debit Adjustment','Transfer') NOT NULL, amount int(11) NOT NULL, deleted datetime DEFAULT NULL, deleted_by int(11) DEFAULT NULL ENGINE=InnoDB DEFAULT CHARSET=utf8; ADD KEY type (type), ADD KEY deleted (deleted)

我会在这里提供任何线索!

4 个答案:

答案 0 :(得分:1)

我使用“解释”来检查以上查询是否可以使用索引。 结果,该索引不适用于“ OR”运算符或“ IN”, 所以我认为“ UNION”是更好的选择。 而且我认为您不需要为“已删除”列添加索引,因为它不能正常工作。

IN运算符的“解释”结果: "explain" result for IN operator

“或”运算符的“解释”结果: "explain" result for OR operator

“联盟”结果: "union" result

“已删除”列上的索引不起作用: index on "deleted" column doesn't work

答案 1 :(得分:0)

(编辑:显然,在这种情况下这是错误的。仅当OR'd条件涉及不同的字段时,此答案才适用。...或创建范围检查,以防止利用更远的字段进入索引。请参阅注释有关详细信息。)

在出现OR条件时,MySQL不能很好地利用索引。通常,您可以加快

之类的查询
SELECT a FROM b WHERE y = n1 OR y = n2

通过将其扩展为这样的联合体

SELECT a FROM b WHERE y = n1
UNION 
SELECT a FROM b WHERE y = n2

我听说更多的最新版本使以y IN (n1, n2)形式表示的条件更加有效,但是最近几年我的主要工作是使用MS SQL,所以我不能说如何它已经改善了很多。

这甚至可以用于您的直接求和再扩展一点的情况。...

SELECT SUM(subt) 
FROM (
   SELECT SUM(amount)/100 AS subt FROM transactions WHERE type="Payment" and deleted is NULL
   UNION 
   SELECT SUM(amount)/100 AS subt FROM transactions WHERE type="Refund" and deleted is NULL
) AS subq

答案 2 :(得分:0)

我认为我想出了一个合理的想法,为什么使用索引列会导致延迟。问题应该出在该列的数据中,尤其是在其唯一值的格式错误-分别是二进制的三个节点。它由480万行相同的NULL值组成,仅30万行具有3 K的唯一值。

  1. 当使用删除的索引查找NULL值时,它对减少MySQL将进一步处理的行的子集没有显著作用,但会增加处理二进制树索引的大量开销活动。我怀疑没有索引求和操作就足够快,以至于即使进行全表扫描,它的性能也要好于索引可以提供的行子集减少的好处,但代价是索引开销很大。

  2. 该已删除列中的数据会抽出已删除的索引基数,并使优化器优于基数仅为10的类型列索引。如果两个列中的值分布均是正常的,则逻辑上优先使用一个具有较高基数的子集,从而产生较小的子集以进行进一步处理。但是,此删除的列值的分布对空值的格式非常不正确。以与上述相同的方式,使用删除的索引查找空值会增加很多开销,但对性能影响不大,阻止使用其他更相关的索引,从而导致延迟。

答案 3 :(得分:0)

如果您仅删除deleted上的索引并添加此“复合”索引:

INDEX(deleted, type)   -- in this order

它可能运行得更快。请注意,=列排在第一位(计数为IS NULL,然后是IN(您的OR变成了)。

甚至更快的方法是使索引“覆盖”:

INDEX(deleted, type, amount)   -- in this order

OR转换为UNION是一个很好的技巧,但这不是必需的。

如果deleted很少是NULL,则优化器可能更喜欢该索引,即使事实证明效率较低。 (这可以解释您提出的问题。我的综合索引可以避免此问题。)

独立问题:为什么deleted?您不能简单地将deleted_by用作NULL来表示同一件事吗?