【问题标题】:Query performance on primary index vs index主索引与索引的查询性能
【发布时间】:2019-07-18 08:43:02
【问题描述】:

我有一个关于 mysql 的表和两个性能完全不同的查询。我已经提取了查询计划,但我无法完全理解性能差异背后的原因。

桌子:

+-------------+----------------------------------------------+------------------------------------+
|   TableA    |                                              |                                    |
+-------------+----------------------------------------------+------------------------------------+
| id          | int(10)   unsigned NOT NULL AUTO_INCREMENT   |                                    |
| userId      | int(10)                                      | unsigned DEFAULT NULL              |
| created     | timestamp                                    | NOT NULL DEFAULT CURRENT_TIMESTAMP |
| PRIMARY KEY | id                                           |                                    |
| KEY userId  | userId                                       |                                    |
| KEY created | created                                      |                                    |
+-------------+----------------------------------------------+------------------------------------+

键/索引:id 字段上的主键,userId 字段 ASC 上的键 ,created 字段 ASC 上的另一个键。

tableA 是一个很大的表,它包含数百万行。

我在这张表上运行的查询是:

id为1234的用户在这个表中有150万条记录。我想获取其最新的 100 行。为了实现这一点,我有 2 个不同的查询:

查询 1:

SELECT * FROM tableA USE INDEX (userId) 
WHERE userId=1234 ORDER BY created DESC LIMIT 100;

查询 2:

SELECT * FROM tableA 
WHERE userId=1234 ORDER BY id DESC LIMIT 100;

由于tableAid 字段是自动递增的,所以保持最新的条件。这 2 个查询返回相同的结果。但是,存在巨大的性能差异。

查询计划是:

+----------+-----------------------------------------------+-------------------------------+------+---------------------------------------+
| Query No |                   Operation                   |            Params             | Raws |               Raw desc                |
+----------+-----------------------------------------------+-------------------------------+------+---------------------------------------+
| Query 1  | Sort(using file sort) Unique index scan (ref) | table: tableA; index: userId; | 2.5M | Using index condition; Using filesort |
| Query 2  | Unique index scan (ref)                       | table: tableA; index: userId; | 2.5M | Using where                           |
+----------+-----------------------------------------------+-------------------------------+------+---------------------------------------+


+--------+-------------+
|        | Performance |
+--------+-------------+
| Query1 | 7,5 s       |
+--------+-------------+
| Query2 | 741 ms      |
+--------+-------------+

我了解到Query 1 有一个排序操作。在每个查询中,使用的索引是userId。但是为什么查询 2 中没有使用排序呢?主索引如何影响?

Mysql 5.7

编辑:表格上有更多列,我从上面的表格定义中提取出来。

【问题讨论】:

  • 您忘记告诉我们您已经在此表上创建了哪些索引。
  • 这两个查询的相对性能如何?
  • @TimBiegeleisen 我已经在表格中添加了性能数据和索引。
  • 性能一点也不意外。 MySQL 不想使用索引userId,所以当你强制它时,性能会下降。请参阅我的答案以获取应该可以正常工作的索引。
  • @TimBiegeleisen 我支持你的警告。但我不确定您是否已阅读我在答案下的评论。如果我没有明确说索引,那么查询就会超时,它永远不会返回。它的性能更差。因此,您可以在没有“使用索引”字段的情况下考虑我的问题。主要问题仍然有效。

标签: mysql performance innodb


【解决方案1】:

由于tableA的id字段是自增的,所以保持最新的条件。

通常是一个有效的陈述。

WHERE userId=1234 ORDER BY created DESC LIMIT 100

需要这个“复合”索引:(userId, created)。这样一来,无论表格大小或该用户的行数如何,它都只会命中 100 行。

同样的道理

WHERE userId=1234 ORDER BY id DESC LIMIT 100;

即它需要(userId, id)。但是,在 InnoDB 中,当您说 INDEX(x) 时,它会默默地添加 PRIMARY KEY 列。所以你有效地得到INDEX(x,id)。这就是为什么您的普通 INDEX(userId) 效果很好的原因。

EXPLAIN 很少(如果有的话)考虑LIMIT。这就是为什么两个查询的“行”都是“2.5M”的原因。

如果您取出USE INDEX 提示,第一个查询可能(或可能不)使用了INDEX(userId)。选择取决于表中userId = 1234 的百分比。如果小于约 20%,将使用该指数。但它会在二级索引和数据之间来回反弹——都是 150 万次。如果超过 20%,它将通过简单地读取所有“数百万”行来避免弹跳,忽略那些不适用的行。

注意:您在第一季度所拥有的内容仍将读取至少 150 万行,对它们进行排序(“使用文件排序”),然后剥离所需的 100 行。但是使用INDEX(userId, created),它可以跳过排序并只查看100 行。

如果没有看到SHOW CREATE TABLE 和未注释的EXPLAIN,我无法解释“唯一索引扫描”。 (EXPLAIN FORMAT=JSON SELECT... 可能会提供更多见解。)

【讨论】:

  • 感谢您的回答。它确实具有解释性,并提供了更多见解。另外,对于您关于表格列数的问题:是的,表格上有更多列。我已经更新了问题。
  • @Shnkc - 由于表格中的列较多,加上您使用了* (SELECT *),Tim 关于“覆盖”的 cmets 不适用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-12
  • 1970-01-01
  • 1970-01-01
  • 2015-03-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多