【问题标题】:Why Index Usage Depend On Column Type为什么索引使用取决于列类型
【发布时间】:2014-09-29 18:59:29
【问题描述】:

这是我的查询:

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 优化器正在使用它的提示,如下所示:

但是,只有当 'value' 列(type 'varchar')长度为 每当我将此列修改为更高的长度时,索引的“值”长度仅保持最大 255(假设是,我并不担心)并且 mysql 优化器完全丢弃索引(相反,它使用 property_id 外键索引)。解释命令现在显示:

所以,我的问题是:

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

【问题讨论】:

  • 最大索引长度为 255

标签: mysql sql optimization indexing


【解决方案1】:

查看第一个执行计划并尝试了解它是如何使用索引的。

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

在哪里使用

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

key_len = 4

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

使用索引

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

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

因此,当您尝试对不适合索引的内容进行索引时,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 消失(尽管有时不会)。

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

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

参考文献

【讨论】:

  • 感谢您提供详细信息!我终于找到了一种方法来获取 mysql 优化器使用的索引(通过按照您的建议更改顺序)。但有趣的是,性能还是和之前的指数一样,一点改善都没有!对此有何建议?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-05
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
  • 1970-01-01
相关资源
最近更新 更多