慢速MySql查询与索引限制的顺序

时间:2015-11-06 12:09:04

标签: mysql entity-framework

我有一个由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

它运行缓慢。 我有文件的索引.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;

您会注意到文件表中还有其他一些关系,我只是为了简化查询而忽略了观察到的行为。

以下是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时才会出现此问题。 此外,我在修改查询方面做得不多,因为它是由Entity Framework生成的。为了简化它,我尽可能多地使用LINQ查询(起初它有一些可怕的子查询执行得更慢)。

查询提示(如强制索引中)也不是解决方案,因为EF不支持此类功能。

我主要希望找到一些数据库级优化。

对于那些没有发现标签的人来说,有问题的数据库是MySql。

2 个答案:

答案 0 :(得分:0)

MySQL每个表只使用一个索引。现在,它更喜欢使用外键索引,因此连接是有效的,但这意味着排序不使用索引。

尝试在ContainerId, filedID

上创建复合索引

答案 1 :(得分:0)

这基本上是您的查询:

SELECT e1.*
FROM `files` e1 INNER JOIN
     `containers` e2
     ON e1.`ContainerId` = e2.`Id`
ORDER BY e1.`Id` ASC
LIMIT 0, 10;

您可以尝试 files(id, ContainerId)上的索引。这可能会激发MySQL使用复合索引,专注于order by

如果查询被表述为:

,则可能更有可能
SELECT e1.*
FROM `files` e1
WHERE EXISTS (SELECT 1 FROM containers e2 WHERE e1.`ContainerId` = e2.`Id`)
ORDER BY e1.`Id` ASC
LIMIT 0, 10;

有一种方法可以使用索引。但是,它取决于MySQL中未记录的内容(尽管它在实践中有效)。下面将按顺序读取数据,但它会产生实现子查询的开销 - 但不是为了排序:

SELECT e1.*
FROM (SELECT e1.*
      FROM files e1
      ORDER BY e1.id ASC
     ) e1
WHERE EXISTS (SELECT 1 FROM containers e2 WHERE e1.`ContainerId` = e2.`Id`)
LIMIT 0, 10;