我有一个简单的mysql查询,但是当我有很多记录(目前为103,0000)时,性能非常慢并且它说它正在使用filesort,我不确定这是否是为什么它很慢。有没有人建议加快它?或使用filesort停止它?
MYSQL查询:
SELECT adverts .*
FROM adverts
WHERE (
price >='0'
)
AND (
adverts.status = 1
)
AND (
adverts.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 AUTO_INCREMENT=103878 DEFAULT CHARSET=utf8
答案 0 :(得分:23)
问题是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
字段上,不属于密钥price
。
要更快地查找行并更快地对行进行排序,您需要添加一个索引,其中包含WHERE
和ORDER BY
子句中使用的所有字段:
ALTER TABLE `adverts` ADD INDEX status_approved_date_updated(`status`, `approved`, `date_updated`);
用于排序的字段必须位于索引的最后位置。在索引中包含price
是没用的,因为查询中使用的条件将返回一系列值。
如果EXPLAIN
仍然显示它正在使用filesort,您可以尝试强制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优化器通常会做出正确的选择。但有时它会做出错误的选择,或者不是最好的选择。您需要运行一些测试,看它是否能提高性能。
答案 1 :(得分:2)
删除'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
答案 2 :(得分:1)
我有两个建议。首先,删除where子句中零点周围的引号。该行应该是:
price >= 0
其次,创建这个索引:
CREATE INDEX `helper` ON `adverts`(`status`,`approved`,`price`,`date_created`);
这应该允许MySQL通过仅使用索引来查找LIMIT子句指定的10行。 Filesort本身并不是一件坏事...需要处理的行数是。
答案 3 :(得分:1)
您的WHERE
条件使用price
,status
,approved
进行选择,然后使用date_updated
进行排序。
所以你需要一个带有这些字段的单个索引;我建议按此顺序对approved
,status
,price
和date_updated
进行索引。
一般规则是首先放置WHERE等式,然后放置范围(大于,小于或等于,等等),最后排序字段。 (请注意,为了这个目的,将一个字段留下可能会使索引不太可用,甚至无法使用)。
CREATE INDEX advert_ndx ON adverts (approved, status, price, date_updated);
这样,只有在LIMIT
发挥作用之后才需要访问表数据,并且只会慢速检索少量记录。
我还会删除所有不需要的索引,这会加快INSERT
和UPDATE
的速度。
答案 4 :(得分:0)
MySQL没有使用密钥date_updated
进行排序,只是使用price
子句中使用的WHERE
密钥。您可以尝试使用索引提示:
http://dev.mysql.com/doc/refman/5.1/en/index-hints.html
添加类似
的内容USE KEY FOR ORDER BY (date_updated)