这是我的桌子:
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 基于成本的优化器有时很微妙,似乎有自己的规则。
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”。