MySQL 没有使用复合索引的所有关键部分

MySQL not using all key parts of composite index

我有一个 InnoDB table 在 MySQL 5.7.19 中包含约 170 万行。我想优化以下查询:

select * from `table` where `col1` = 'x' and `col2` = 123 and `col3` = 'z'

其中列定义为(全部使用 utf8mb4 编码):

col1 varchar(255) null
col2 varchar(255) not null
col3 varchar(255) not null

以及所有列的索引:

key (
  col1, -- Cardinality: 40
  col2, -- Cardinality: 472810
  col3  -- Cardinality: 403767
)

我希望查询 运行 快,因为 MySQL 应该能够充分利用索引。现在,性能不是很好,当我 运行 使用 explain format=json:

进行查询时,它开始变得有意义了
"used_key_parts": [
   "col1"
],
"key_length": "1022"

只使用了复合索引的第一列。使用 table 扫描评估 col2col3 上的约束。

谁能给我解释一下这是怎么回事,并就如何改进提出建议?

我目前通过将列合并为一列来解决它,方法是引入并索引一个存储的生成列,该列连接 col1col2。但是,我不能将它用于想要在这些列上使用 IN() 运算符的查询。

提前致谢!

亚诺

尝试使用高基数列构建组合

例如:

col2,col3,col1 

并在条件

不需要 () 的地方使用 AND 运算符这一事实
  select * from `table` where `col1` = 'x' and `col2` = 'y' and `col3` = 'z' 

最后您可以使用 FORCE

强加索引

谢谢大家的回复。在使用索引中的列顺序(与基数相关)后,我注意到应用程序正在与 col2(这是一个 varchar 列)上的整数值进行比较。将值转换为字符串解决了性能问题。

`col2` = 123

是你的毁灭。将 VARCHAR 与整数常量进行比较时,varchar 将转换为数字。这需要即时转换 所有 相关行。

毕竟,col2 可以包含 "0123""123.0""1.23e2"。作为字符串,它们是完全不同的; varchars 上的索引根据 string 属性 (COLLATION).

排序

可能的解决方案是更改为添加引号:

`col2` = "123"

WHEREANDs 的顺序无关紧要。

INDEX 中列的顺序很重要。先有一个 INDEXcol1col3,无论顺序如何,都会更好。

比较 INDEX(col1)INDEX(col3) 时基数确实很重要。

基数对于实际使用的索引部分 重要,例如比较 INDEX(col1, col3)INDEX(col3, col1).

相比之下,WHERE int_col = "123" 转换"123"123可以使用索引。