【发布时间】:2015-11-06 12:09:04
【问题描述】:
我有一个由 Entity Framework 生成的查询,如下所示:
SELECT
`Extent1`.`Id`,
`Extent1`.`Name`,
`Extent1`.`ExpireAfterUTC`,
`Extent1`.`FileId`,
`Extent1`.`FileHash`,
`Extent1`.`PasswordHash`,
`Extent1`.`Size`,
`Extent1`.`TimeStamp`,
`Extent1`.`TimeStampOffset`
FROM `files` AS `Extent1` INNER JOIN `containers` AS `Extent2` ON `Extent1`.`ContainerId` = `Extent2`.`Id`
ORDER BY
`Extent1`.`Id` ASC LIMIT 0,10
它运行缓慢。 我有关于 files.Id (PK)、files.ContainerId(FK)、containers.Id(PK) 的索引,我不明白为什么 mysql 在返回所需记录之前似乎要进行完整排序,即使已经有Id 列上的索引。
此外,这些数据显示在支持过滤器、排序和分页的网格中,并且非常需要很好地使用索引。
以下是表定义:
CREATE TABLE `files` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`FileId` varchar(100) NOT NULL,
`ContainerId` int(11) NOT NULL,
`ContainerGuid` binary(16) NOT NULL,
`Guid` binary(16) NOT NULL,
`Name` varchar(1000) NOT NULL,
`ExpireAfterUTC` datetime DEFAULT NULL,
`PasswordHash` binary(32) DEFAULT NULL,
`FileHash` tinyblob NOT NULL,
`Size` bigint(20) NOT NULL,
`TimeStamp` double NOT NULL,
`TimeStampOffset` double NOT NULL,
`FilePostId` int(11) NOT NULL,
`FilePostGuid` binary(16) NOT NULL,
`AttributeId` int(11) NOT NULL,
PRIMARY KEY (`Id`),
UNIQUE KEY `FileId_UNIQUE` (`FileId`),
KEY `Files_ContainerId_FK` (`ContainerId`),
KEY `Files_AttributeId_FK` (`AttributeId`),
KEY `Files_FileId_index` (`FileId`),
KEY `Files_FilePostId_index` (`FilePostId`),
KEY `Files_Guid_index` (`Guid`),
CONSTRAINT `Files_AttributeId_FK` FOREIGN KEY (`AttributeId`) REFERENCES `attributes` (`Id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `Files_ContainerId_FK` FOREIGN KEY (`ContainerId`) REFERENCES `containers` (`Id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `Files_FilePostsId_FK` FOREIGN KEY (`FilePostId`) REFERENCES `fileposts` (`Id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=977942 DEFAULT CHARSET=utf8;
CREATE TABLE `containers` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NOT NULL,
`Guid` binary(16) NOT NULL,
`AesKey` binary(32) NOT NULL,
`FileCount` int(10) unsigned NOT NULL DEFAULT '0',
`Size` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`Id`),
KEY `Containers_Guid_index` (`Guid`),
KEY `Containers_Name_index` (`Name`)
) ENGINE=InnoDB AUTO_INCREMENT=76 DEFAULT CHARSET=utf8;
您会注意到 files 表中还有一些其他关系,我省略了这些关系只是为了简化查询而不影响观察到的行为。
这也是 EXPLAIN EXTENDED 的输出:
+----+-------------+---------+-------+----------------------+-----------------------+---------+----------------------------------+-------+----------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+-------+----------------------+-----------------------+---------+----------------------------------+-------+----------+----------------------------------------------+
| 1 | SIMPLE | Extent2 | index | PRIMARY | Containers_Guid_index | 16 | NULL | 9 | 100.00 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | Extent1 | ref | Files_ContainerId_FK | Files_ContainerId_FK | 4 | netachmentgeneraltest.Extent2.Id | 73850 | 100.00 | |
+----+-------------+---------+-------+----------------------+-----------------------+---------+----------------------------------+-------+----------+----------------------------------------------+
文件表有大约 900000 条记录(并且还在计数),容器有 9 条。 仅当存在 ORDER BY 时才会出现此问题。 此外,我无法在修改查询方面做太多事情,因为它是由实体框架生成的。为了简化它,我尽可能多地使用 LINQ 查询(起初它有一些执行速度更慢的可怕子查询)。
查询提示(如强制索引)在这里也不是解决方案,因为 EF 不支持此类功能。
我主要希望找到一些数据库级别的优化来做。
对于那些没有发现标签的人来说,有问题的数据库是 MySql。
【问题讨论】:
-
你能不能尝试一个 STRAIGHT_JOIN 来强制它加入表的顺序。希望允许它使用 files.id 上的索引进行排序。
-
@Kickstart 确实,straight_join 有效!结果是立竿见影的。但是如何在实体框架中使用这个呢?有什么线索吗?
-
怕没有经验。然而,在每个表上运行 ANALYZE TABLE .... 可能是值得的。可能是细节已过时,因此选择了低效的加入顺序
-
@Kickstart 我在两张桌子上都运行了 ANALYZE TABLE,但仍然没有乐趣。
-
如果容器只有9行,可以去掉KEY
Files_ContainerId_FK(ContainerId)吗?