17

我有一个简单的 mysql 查询,但是当我有很多记录(当前为 103,0000)时,性能真的很慢,它说它正在使用文件排序,我不确定这是否是它很慢的原因。有没有人有任何建议来加快它?或使用文件排序停止它?

MYSQL 查询:

SELECT *    
FROM adverts
WHERE (price >= 0)
    AND (status = 1)
    AND (approved = 1)
ORDER BY date_updated DESC 
LIMIT 19990, 10

解释结果:

id   select_type   table   type    possible_keys    key    key_len    ref    rows   Extra 
1    SIMPLE        adverts range   price            price  4          NULL   103854 Using where; Using filesort

这是广告表和索引:

CREATE TABLE `adverts` (
  `advert_id` int(10) NOT NULL AUTO_INCREMENT,
  `user_id` int(10) NOT NULL,
  `type_id` tinyint(1) NOT NULL,
  `breed_id` int(10) NOT NULL,
  `advert_type` tinyint(1) NOT NULL,
  `headline` varchar(50) NOT NULL,
  `description` text NOT NULL,
  `price` int(4) NOT NULL,
  `postcode` varchar(7) NOT NULL,
  `town` varchar(60) NOT NULL,
  `county` varchar(60) NOT NULL,
  `latitude` float NOT NULL,
  `longitude` float NOT NULL,
  `telephone1` varchar(15) NOT NULL,
  `telephone2` varchar(15) NOT NULL,
  `email` varchar(80) NOT NULL,
  `status` tinyint(1) NOT NULL DEFAULT '0',
  `approved` tinyint(1) NOT NULL DEFAULT '0',
  `date_created` datetime NOT NULL,
  `date_updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `expiry_date` datetime NOT NULL,
  PRIMARY KEY (`advert_id`),
  KEY `price` (`price`),
  KEY `user` (`user_id`),
  KEY `type_breed` (`type_id`,`breed_id`),
  KEY `headline_keywords` (`headline`),
  KEY `date_updated` (`date_updated`),
  KEY `type_status_approved` (`advert_type`,`status`,`approved`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
4

5 回答 5

41

问题是 MySQL 在执行查询时只使用一个索引。如果您在WHERE子句中添加一个使用 3 个字段的新索引,它将更快地找到行。

ALTER TABLE `adverts` ADD INDEX price_status_approved(`price`, `status`, `approved`);

根据 MySQL 文档ORDER BY Optimization

在某些情况下,MySQL 不能使用索引来解析 ORDER BY,尽管它仍然使用索引来查找与 WHERE 子句匹配的行。这些情况包括:
用于获取行的键与 ORDER BY 中使用的键不同。

这就是你的情况。正如 的输出EXPLAIN告诉我们的那样,优化器使用键price来查找行。但是,ORDER BY是在date_updated不属于 key 的字段上price

要更快地找到行并更快地对行进行排序,您需要添加一个索引,其中包含子句WHERE中使用的所有字段和ORDER BY子句:

ALTER TABLE `adverts` ADD INDEX status_approved_date_updated(`status`, `approved`, `date_updated`);

用于排序的字段必须位于索引中的最后一个位置。包含price在索引中是没有用的,因为查询中使用的条件会返回一个范围的值。

如果EXPLAIN仍然显示它正在使用文件排序,您可以尝试强制 MySQL 使用您选择的索引:

SELECT adverts.*
FROM adverts
FORCE INDEX(status_approved_date_updated)
WHERE price >= 0
AND adverts.status = 1
AND adverts.approved = 1
ORDER BY date_updated DESC 
LIMIT 19990, 10

通常不需要强制索引,因为 MySQL 优化器通常会做出正确的选择。但有时它会做出错误的选择,或者不是最佳选择。您将需要运行一些测试以查看它是否提高了性能。

于 2012-08-27T20:20:59.707 回答
3

删除周围的刻度'0'- 它目前可能会阻止使用索引,但我不确定。尽管如此,它还是更好的风格,因为价格是int类型而不是字符列。

SELECT adverts .*    
FROM adverts
WHERE (
price >= 0
)
AND (
adverts.status = 1
)
AND (
adverts.approved = 1
)
ORDER BY date_updated DESC 
LIMIT 19990 , 10
于 2012-08-27T21:26:34.423 回答
1

您的WHERE条件使用price, status,approved进行选择,然后date_updated用于排序。

所以你需要一个包含这些字段的索引;我建议按此顺序对 , 和 ,approved进行索引statuspricedate_updated

一般规则是首先放置 WHERE 等式,然后是范围(大于、小于或等于、介于等),最后对字段进行排序。(请注意,省略一个字段可能会降低索引的可用性,甚至无法用于此目的)。

CREATE INDEX advert_ndx ON adverts (approved, status, price, date_updated);

这样,只有在发挥了LIMIT它的魔力之后才需要访问表数据,并且您只会缓慢检索少量记录。

我还将删除任何不需要的索引,这将加速INSERTs 和UPDATEs。

于 2012-08-27T22:04:06.943 回答
1

MySQL 不使用键date_updated进行排序,而只是使用在子句中使用的price键。WHERE您可以尝试使用索引提示:

http://dev.mysql.com/doc/refman/5.1/en/index-hints.html

添加类似的东西

USE KEY FOR ORDER BY  (date_updated)
于 2012-08-27T20:37:24.270 回答
1

我有两个建议。首先,删除 where 子句中零周围的引号。那行应该是:

price >= 0

其次,创建这个索引:

CREATE INDEX `helper` ON `adverts`(`status`,`approved`,`price`,`date_created`);

这应该允许 MySQL 仅使用索引来查找 LIMIT 子句指定的 10 行。Filesort 本身并不是一件坏事……需要处理的行数是。

于 2012-08-27T21:46:07.003 回答