我到了一个要点,就是当我在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)
我会在这里提供任何线索!
答案 0 :(得分:1)
我使用“解释”来检查以上查询是否可以使用索引。 结果,该索引不适用于“ OR”运算符或“ IN”, 所以我认为“ UNION”是更好的选择。 而且我认为您不需要为“已删除”列添加索引,因为它不能正常工作。
IN运算符的“解释”结果:
“或”运算符的“解释”结果:
“联盟”结果:
“已删除”列上的索引不起作用:
答案 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的唯一值。
当使用删除的索引查找NULL值时,它对减少MySQL将进一步处理的行的子集没有显著作用,但会增加处理二进制树索引的大量开销活动。我怀疑没有索引求和操作就足够快,以至于即使进行全表扫描,它的性能也要好于索引可以提供的行子集减少的好处,但代价是索引开销很大。
该已删除列中的数据会抽出已删除的索引基数,并使优化器优于基数仅为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
来表示同一件事吗?