MySQL 显然没有使用多列索引

问题描述 投票:0回答:1

这是我的桌子:

CREATE TABLE `Score` (
  `id` int NOT NULL AUTO_INCREMENT,
  `playerId` int NOT NULL,
  `points` int NOT NULL,
  `gameId` int NOT NULL,
  `isDeleted` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `Score_playerId_fkey` (`playerId`),
  KEY `Score_gameId_fkey` (`gameId`),
  CONSTRAINT `Score_gameId_fkey` FOREIGN KEY (`gameId`) REFERENCES `Game` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE,
  CONSTRAINT `Score_playerId_fkey` FOREIGN KEY (`playerId`) REFERENCES `Player` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=813 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

这是我正在尝试优化的查询:

SELECT *
FROM `Score`
WHERE `Score`.`gameId` = 2 AND `Score`.`isDeleted` = 0
ORDER BY `Score`.`points` DESC
LIMIT 50 OFFSET 0;

这是此查询的

EXPLAIN
的输出:

id  select_type table   partitions  type    possible_keys       key                 key_len ref     rows    filtered    Extra
1   SIMPLE      Score   NULL        ref     Score_gameId_fkey   Score_gameId_fkey   4       const   160     100.00      Using where; Using filesort

我从中了解到(if我正确地解释了这个输出)是有一个

WHERE
过滤器应用于160行。 事实上,表中正好有 160 行带有
gameId = 2
:

SELECT COUNT(*) FROM Score WHERE gameId = 2;
COUNT(*)
160

我想,在

gameId
上使用外键索引后,将逐一检查结果行中
isDeleted
字段的值。 因此,我尝试添加这两个索引:

CREATE INDEX gameId_isDeleted ON `Score` (`gameId`, `isDeleted`);
CREATE INDEX isDeleted ON `Score` (`isDeleted`);

但是,在这些更改之后,

EXPLAIN
的输出仍然显示仅使用了 ,因为
possible_keys
表示
Score_gameId_fkey,gameId_isDeleted,isDeleted
key
仍然表示
Score_gameId_fkey
:

id  select_type table   partitions  type    possible_keys                                   key                 key_len ref     rows    filtered    Extra
1   SIMPLE      Score   NULL        ref     Score_gameId_fkey,gameId_isDeleted,isDeleted    Score_gameId_fkey   4       const   160     100.00      Using where; Using filesort

如果仅存在

gameId_isDeleted
isDeleted
索引,或两者都存在,则没有区别(我通过删除并重新创建它们来尝试所有可能的组合)。

我是否正确解释了这两个

EXPLAIN
语句的结果?如果是这样,我怎样才能最好地优化查询?

此外,我知道

points
字段上的索引也可以通过避免
filesort
引起的
ORDER BY
来改善情况,对吗?

mysql performance query-optimization
1个回答
0
投票

MySQL 基于成本的优化器有时很微妙,似乎有自己的规则。

  • 估计它不会从
    gameId
    isDeleted
    上的复合索引获得太多好处,因为
    isDeleted=0
    无论如何都会匹配大多数(或全部)行。
  • 因此它更喜欢
    gameId
    上的单列索引,因为它是一个“较小”的索引。 IE。列更少,因此每页可以容纳更多条目,因此将索引加载到缓冲池中的成本更低。

您可以在 gameId

删除
单列索引。任何使用该索引的查询也可以使用复合索引,其中
gameId
是第一列。这将简化优化器的选择。

您可以添加

points
作为此索引的第三列。这将有助于使
ORDER BY points
成为无操作,因为优化器知道对于给定的
points
gameId
值,索引中的条目已经按
isDeleted
排序。也就是说,对于
gameId=2 and isDeleted=0
,匹配这两个条件的条目都是绑定的,如果索引的第三列是
points
,那么根据定义,这些绑定按
points
排序。因此可以简单地按索引顺序读取条目,无需进行排序。您应该会看到 EXPLAIN 中省略了“Using filesort”。

© www.soinside.com 2019 - 2024. All rights reserved.