为什么索引使用取决于列类型

时间:2014-08-06 18:10:22

标签: mysql sql optimization indexing

这是我的问题:

SELECT u0_.value AS value0, u1_.property_uri AS property_uri1, count(u0_.id) AS sclr2, u2_.service_id AS sclr3 
FROM usc_connection_triple u0_ 
INNER JOIN usc_pro1_ ON u0_.property_id = u1_.id AND (u1_.status = 1) 
INNER JOIN usc_account_connection u3_ ON u0_.account_connection_id = u3_.id AND (u3_.status = 1) 
INNER JOIN usc_service_subscriber u2_ ON ((u2_.id = u3_.account_1_id OR u2_.id = u3_.account_2_id)) AND (u2_.status = 1) 
WHERE (u1_.create_analytics = '1') AND (u0_.status = 1) GROUP BY u2_.service_id, u0_.property_id, u0_.value;

我在u0_(usc_connection_triple)上创建了一个索引,其定义如下:

CREATE INDEX `temp` ON usc_connection_triple(property_id, account_connection_id, status, value);

这个综合指数运作良好,解释'命令还显示了mysql优化器使用它的提示,如下所示:

enter image description here

但是,只有当'价值'列(类型' varchar')长度为< = 255。 每当我将此列修改为更高的长度时,索引的值为'长度只保留255最大值(假设是,我并不担心)和 mysql优化器完全丢弃索引(相反,它使用property_id外键索引)。 explain命令现在显示:

Explain Command Result

所以,我的问题是:

  • 为什么mysql优化器会丢弃这个?
  • 除了' USE INDEX' FORCE INDEX'以外,还有其他更好的方法可以通过修改索引来使这个索引正常工作吗?命令?
  • 我可以使用三列索引来丢弃第四个值'柱?我试过了,但似乎还没有被使用。

1 个答案:

答案 0 :(得分:1)

查看第一个执行计划,并尝试了解 使用索引的方式。

特别是额外的列提供了非常有价值的信息:

使用位置

这意味着它需要将一些where子句应用为过滤谓词。即,它并没有真正使用所有where子句的索引,只是其中一些。

key_len = 4

key_len列中,MySQL告诉我们它有效地使用了多少索引。 4表示4个字节,通常转换为单个int(或类似)列。这意味着,MySQL只能有效地使用索引中的第一列(property_id)。请参阅下面的修复建议。

使用索引

返回 Extra 列。它实际上应该是“仅使用索引”。这意味着索引恰好具有此查询所需的所有数据(列)。换句话说,查询不引用任何不属于索引的列。因此,MySQL不需要进行额外的IO操作来从实际表中获取更多列。此功能也称为仅索引扫描。它可以提高百倍的查询性能。

现在出现了@juergend提到的限制:索引条目的最大长度是有限的。对于InnoDB,每列767个字节,总共3072个字节。但是,如果您使用的是多字节字符集(UTF-8),这些是 bytes ,数字较小 - 正如您所观察到的那样。

因此,当您尝试索引不适合索引的内容时,MySQL将默默地截断索引条目以适应。但是,这意味着它不再存储索引中的完整列,因此它需要对表进行额外跳转以获取完整列。这可以轻松地将查询速度降低100倍:(

最后,最好不要使用这个索引,或者可能是另一个恰好更小的索引(如你的情况)。

<强>建议

首先修复using where部分。查看你的连接谓词:

INNER JOIN usc_pro1_ ON u0_.property_id = u1_.id AND (u1_.status = 1) 

和索引

ON usc_connection_triple(property_id, account_connection_id, status, value)

只能在左侧列上使用高效索引。想象一下一本骄傲的电话簿 - 通常以姓氏,名字命名。现在尝试在这本电话簿中找到所有名字为“Sarah”的人。这里也发生了类似的问题。第一列property_id很好,在查询中提到了相等条件。但是,where子句中根本没有提到下一个索引列account_connection_id。这就是它可以将下一列status仅用作过滤器的原因。

所以,第一个想法可能是重新排序索引,如下所示:

ON usc_connection_triple(property_id, status, account_connection_id, value)

这会使using where消失(尽管,有时它不会取决于MySQL版本。)

您甚至可以考虑先放置status,因为它似乎是一个始终存在的where子句。这甚至允许在某些情况下使用索引对property_id进行排序(不在您的情况下,因为它不是您的order by子句中的第一列)。

如果您无法使查询执行仅索引扫描(在额外中显示using index),则应从索引中删除where子句中未使用的列。

<强>参考