使用文件排序的 MYSQL 性能变慢
Posted
技术标签:
【中文标题】使用文件排序的 MYSQL 性能变慢【英文标题】:MYSQL performance slow using filesort 【发布时间】:2012-08-22 08:26:57 【问题描述】:我有一个简单的 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
【问题讨论】:
如果省略LIMIT 19990,10
子句,会不会也很慢?如果您将其切换为LIMIT 10
,您可能会获得更快的操作。在这种情况下,您可能想弄清楚如何使用 SELECT
而不是 LIMIT
来检索这些行。
我只添加了索引status_approved
(status
,approved
,date_updated
)。解释现在说它使用 where 而不是 filesort 并且查询需要 0.04 秒而不是 2 秒 + 就像以前一样,我认为这可能已经解决了。
只是一个无关的想法......这些括号都不是必需的。
【参考方案1】:
问题是 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
位于不属于键 price
的字段 date_updated
上。
要更快地找到行并对行进行排序,您需要添加一个索引,其中包含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 优化器通常会做出正确的选择。但有时它会做出错误的选择,或者不是最佳选择。您需要运行一些测试,看看它是否能提高性能。
【讨论】:
我已经做到了,谢谢,它并没有真正加快速度,它仍在使用文件排序。有没有办法使用索引而不是文件排序? 根据您的解释输出,它使用的是“价格”索引。我认为您的 DESC 排序导致了部分问题。 @user1052096:您可以在我的答案中创建索引后发布查询的EXPLAIN
输出吗?
感谢您将我引向 FORCE INDEX - 这是我遇到的问题的救星!【参考方案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
【讨论】:
【参考方案3】:MySQL 不使用键 date_updated
进行排序,而只是使用 price
键,因为它在 WHERE
子句中使用。您可以尝试使用索引提示:
http://dev.mysql.com/doc/refman/5.1/en/index-hints.html
添加类似的东西
USE KEY FOR ORDER BY (date_updated)
【讨论】:
【参考方案4】:我有两个建议。首先,删除 where 子句中零周围的引号。那行应该是:
price >= 0
其次,创建这个索引:
CREATE INDEX `helper` ON `adverts`(`status`,`approved`,`price`,`date_created`);
这应该允许 MySQL 仅使用索引来查找 LIMIT 子句指定的 10 行。 Filesort 本身并不是一件坏事……需要处理的行数是。
【讨论】:
【参考方案5】:您的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
s 和 UPDATE
s 的速度。
【讨论】:
以上是关于使用文件排序的 MYSQL 性能变慢的主要内容,如果未能解决你的问题,请参考以下文章