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 扫描评估 col2
和 col3
上的约束。
谁能给我解释一下这是怎么回事,并就如何改进提出建议?
我目前通过将列合并为一列来解决它,方法是引入并索引一个存储的生成列,该列连接 col1
和 col2
。但是,我不能将它用于想要在这些列上使用 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"
WHERE
中 ANDs
的顺序无关紧要。
INDEX
中列的顺序很重要。先有一个 INDEX
和 col1
和 col3
,无论顺序如何,都会更好。
比较 INDEX(col1)
与 INDEX(col3)
时基数确实很重要。
基数对于实际使用的索引部分 不 重要,例如比较 INDEX(col1, col3)
与 INDEX(col3, col1)
.
相比之下,WHERE int_col = "123"
将转换"123"
到123
可以使用索引。
我有一个 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 扫描评估 col2
和 col3
上的约束。
谁能给我解释一下这是怎么回事,并就如何改进提出建议?
我目前通过将列合并为一列来解决它,方法是引入并索引一个存储的生成列,该列连接 col1
和 col2
。但是,我不能将它用于想要在这些列上使用 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"
WHERE
中 ANDs
的顺序无关紧要。
INDEX
中列的顺序很重要。先有一个 INDEX
和 col1
和 col3
,无论顺序如何,都会更好。
比较 INDEX(col1)
与 INDEX(col3)
时基数确实很重要。
基数对于实际使用的索引部分 不 重要,例如比较 INDEX(col1, col3)
与 INDEX(col3, col1)
.
相比之下,WHERE int_col = "123"
将转换"123"
到123
可以使用索引。