MySQL select 好像很慢但是想不出如何改进?
MySQL select seems very slow but cannot think how to improve?
我有一个包含四列的 table...
`id` INT(11) NOT NULL AUTO_INCREMENT
`tid` INT(11) NOT NULL
`cid` INT(11) NOT NULL
`name` NVARCHAR(4096) NULL DEFAULT NULL
id是唯一主键。其他列不是唯一的。
我想要 return 具有特定 tid 和 cid 的所有 id 值的列表 个值并按名称排序。所以这个...
select id
from myTable
where cid = 1 && tid = 1
order by name
table 中大约有 125k 条记录,应该有大约 50k 条恰好符合此条件。所有四列都有单独的索引。
在我的机器上,查询到 运行 大约需要 140 毫秒。我需要将其降低到 20 毫秒左右或更短。我认为解决方案是添加一个新的覆盖索引,该索引按 cid、tid 和名称的顺序定义。虽然没有任何区别。
有什么想法吗?我的覆盖索引设置不正确吗?
我认为查询和 table 定义本身存在一些问题。
Table.name
是一个 4K 字符列
- 查询按该列排序
您正在根据存储字符串的列进行排序。为了按字符串排序,必须执行字符串比较。字符串比较往往是一个缓慢的操作,并且考虑到您正在使用的列的大小,它很可能会导致明显的性能下降。
我们没有您的 name
栏内容的指示,似乎很难想出一个需要 个字符的实际名称。
如果这个字符串有几条概念上不同的数据,也许应该将该列分解成多个单独的列,如果可能,然后适当地规范化。
如果您可以将该列的内容分成多个较小的内容,然后使用它们,则字符串比较虽然仍然很昂贵,但会 'faster' 仅仅是因为被比较的字符串将明显短于他们现在。
另一件需要考虑的事情是,如果您可以通过 完全避免 字符串比较或通过避免会导致完整 table 扫描的查询来优化搜索,尽管事实上您已经定义了索引。
为此,您应该查看在查询中使用 explain
,以便更好地理解 Query Execution Plan
引用文档(我的重点):
Depending on the details of your tables, columns, indexes, and the
conditions in your WHERE clause, the MySQL optimizer considers many
techniques to efficiently perform the lookups involved in an SQL
query. ... Your goals are ... to learn the SQL syntax and indexing techniques to improve the plan if you see some inefficient operations.
编辑 1
您已阐明您的 name
列实际上是用于用户注释的。在这种情况下,我认为您应该考虑以下内容(在补充中已经提到的内容):
- 将列重命名为与其实际内容相关的名称
- 从列中删除索引
- 不要将该列用于搜索、排序或任何其他操作,而不仅仅是选择它来显示它(这会是非常 恕我直言,如果需要将其用于其他用途,则很少见。)
- 可选地,考虑将列更改为
text
类型,这样您就不必太担心用户 essays 在没有警告的情况下被截断(除非 GUI已对用户强制执行相同的输入长度限制)
INDEX(cid, tid, name)
将大大加快查询速度。
但是,假设 name
是一个文明的长度,例如 255 以下。如果您必须有更长的 name
,那么这是您能做的最好的:
INDEX(cid, tid) -- (in either order)
不,"prefix" 索引不会帮助:INDEX(cid, tid, name(99))
。前缀索引对 ORDER BY
.
没用
All four columns have individual indexes.
单个索引与复合索引不同。有时他们更好;通常他们不是。
我在 Index Cookbook 中提供了更多详细信息。
我有一个包含四列的 table...
`id` INT(11) NOT NULL AUTO_INCREMENT
`tid` INT(11) NOT NULL
`cid` INT(11) NOT NULL
`name` NVARCHAR(4096) NULL DEFAULT NULL
id是唯一主键。其他列不是唯一的。
我想要 return 具有特定 tid 和 cid 的所有 id 值的列表 个值并按名称排序。所以这个...
select id
from myTable
where cid = 1 && tid = 1
order by name
table 中大约有 125k 条记录,应该有大约 50k 条恰好符合此条件。所有四列都有单独的索引。
在我的机器上,查询到 运行 大约需要 140 毫秒。我需要将其降低到 20 毫秒左右或更短。我认为解决方案是添加一个新的覆盖索引,该索引按 cid、tid 和名称的顺序定义。虽然没有任何区别。
有什么想法吗?我的覆盖索引设置不正确吗?
我认为查询和 table 定义本身存在一些问题。
Table.name
是一个 4K 字符列- 查询按该列排序
您正在根据存储字符串的列进行排序。为了按字符串排序,必须执行字符串比较。字符串比较往往是一个缓慢的操作,并且考虑到您正在使用的列的大小,它很可能会导致明显的性能下降。
我们没有您的 name
栏内容的指示,似乎很难想出一个需要 个字符的实际名称。
如果这个字符串有几条概念上不同的数据,也许应该将该列分解成多个单独的列,如果可能,然后适当地规范化。
如果您可以将该列的内容分成多个较小的内容,然后使用它们,则字符串比较虽然仍然很昂贵,但会 'faster' 仅仅是因为被比较的字符串将明显短于他们现在。
另一件需要考虑的事情是,如果您可以通过 完全避免 字符串比较或通过避免会导致完整 table 扫描的查询来优化搜索,尽管事实上您已经定义了索引。
为此,您应该查看在查询中使用 explain
,以便更好地理解 Query Execution Plan
引用文档(我的重点):
Depending on the details of your tables, columns, indexes, and the conditions in your WHERE clause, the MySQL optimizer considers many techniques to efficiently perform the lookups involved in an SQL query. ... Your goals are ... to learn the SQL syntax and indexing techniques to improve the plan if you see some inefficient operations.
编辑 1
您已阐明您的 name
列实际上是用于用户注释的。在这种情况下,我认为您应该考虑以下内容(在补充中已经提到的内容):
- 将列重命名为与其实际内容相关的名称
- 从列中删除索引
- 不要将该列用于搜索、排序或任何其他操作,而不仅仅是选择它来显示它(这会是非常 恕我直言,如果需要将其用于其他用途,则很少见。)
- 可选地,考虑将列更改为
text
类型,这样您就不必太担心用户 essays 在没有警告的情况下被截断(除非 GUI已对用户强制执行相同的输入长度限制)
INDEX(cid, tid, name)
将大大加快查询速度。
但是,假设 name
是一个文明的长度,例如 255 以下。如果您必须有更长的 name
,那么这是您能做的最好的:
INDEX(cid, tid) -- (in either order)
不,"prefix" 索引不会帮助:INDEX(cid, tid, name(99))
。前缀索引对 ORDER BY
.
All four columns have individual indexes.
单个索引与复合索引不同。有时他们更好;通常他们不是。
我在 Index Cookbook 中提供了更多详细信息。