MySQL性能优化:按date时间字段sorting

我有一张大约有100000个博客文章的表格,通过1:n关系链接到一个有50个Feed的表格。 当我使用select语句查询这两个表时,由发布表的datetime字段sorting,MySQL总是使用filesort,导致查询时间非常慢(> 1秒)。 这是postings表(简化)的模式:

 +---------------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | feed_id | int(11) | NO | MUL | NULL | | | crawl_date | datetime | NO | | NULL | | | is_active | tinyint(1) | NO | MUL | 0 | | | link | varchar(255) | NO | MUL | NULL | | | author | varchar(255) | NO | | NULL | | | title | varchar(255) | NO | | NULL | | | excerpt | text | NO | | NULL | | | long_excerpt | text | NO | | NULL | | | user_offtopic_count | int(11) | NO | MUL | 0 | | +---------------------+--------------+------+-----+---------+----------------+ 

这里是feed表:

 +-------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | type | int(11) | NO | MUL | 0 | | | title | varchar(255) | NO | | NULL | | | website | varchar(255) | NO | | NULL | | | url | varchar(255) | NO | | NULL | | +-------------+--------------+------+-----+---------+----------------+ 

以下是执行时间大于1秒的查询。 请注意, post_date字段有一个索引,但是MySQL没有使用它来对发布表进行sorting:

 SELECT `postings`.`id`, UNIX_TIMESTAMP(postings.post_date) as post_date, `postings`.`link`, `postings`.`title`, `postings`.`author`, `postings`.`excerpt`, `postings`.`long_excerpt`, `feeds`.`title` AS feed_title, `feeds`.`website` AS feed_website FROM (`postings`) JOIN `feeds` ON `feeds`.`id` = `postings`.`feed_id` WHERE `feeds`.`type` = 1 AND `postings`.`user_offtopic_count` < 10 AND `postings`.`is_active` = 1 ORDER BY `postings`.`post_date` desc LIMIT 15 

在这个查询中explain extended命令的结果显示MySQL正在使用filesort:

 +----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+ | 1 | SIMPLE | postings | ref | feed_id,is_active,user_offtopic_count | is_active | 1 | const | 30996 | Using where; Using filesort | | 1 | SIMPLE | feeds | eq_ref | PRIMARY,type | PRIMARY | 4 | feedian.postings.feed_id | 1 | Using where | +----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+ 

当我order by部分删除order by ,MySQL停止使用filesort。 请让我知道,如果你有任何想法如何优化这个查询让MySQLsorting和select使用索引的数据。 我已经尝试了一些东西,比如像在一些博客post中提到的那样,在所有的字段/字段上创build一个组合索引,但是这也不起作用。

postings (is_active, post_date)postings (is_active, post_date)顺序)上创build一个复合索引。

它将用于在is_active过滤和按post_datesorting。

MySQL应该在EXPLAIN EXTENDED通过这个索引显示REF访问方法。

请注意,您有一个超过user_offtopic_countRANGE过滤条件,这就是为什么您不能在此字段上使用索引来过滤和按其他字段sorting。

根据你的user_offtopic_countselect性(即多less行满足user_offtopic_count < 10 ),在user_offtopic_count上创build一个索引并让post_datessorting可能会更有用。

为此,在postings (is_active, user_offtopic_count)上创build一个复合索引postings (is_active, user_offtopic_count)并确保使用了通过此索引的RANGE访问方法。

哪个索引会更快取决于您的数据分布。 创build两个索引, FORCE它们,看看哪个更快:

 CREATE INDEX ix_active_offtopic ON postings (is_active, user_offtopic_count); CREATE INDEX ix_active_date ON postings (is_active, post_date); SELECT `postings`.`id`, UNIX_TIMESTAMP(postings.post_date) as post_date, `postings`.`link`, `postings`.`title`, `postings`.`author`, `postings`.`excerpt`, `postings`.`long_excerpt`, `feeds`.`title` AS feed_title, `feeds`.`website` AS feed_website FROM `postings` FORCE INDEX (ix_active_offtopic) JOIN `feeds` ON `feeds`.`id` = `postings`.`feed_id` WHERE `feeds`.`type` = 1 AND `postings`.`user_offtopic_count` < 10 AND `postings`.`is_active` = 1 ORDER BY `postings`.`post_date` desc LIMIT 15 /* This should show RANGE access with few rows and keep the FILESORT */ SELECT `postings`.`id`, UNIX_TIMESTAMP(postings.post_date) as post_date, `postings`.`link`, `postings`.`title`, `postings`.`author`, `postings`.`excerpt`, `postings`.`long_excerpt`, `feeds`.`title` AS feed_title, `feeds`.`website` AS feed_website FROM `postings` FORCE INDEX (ix_active_date) JOIN `feeds` ON `feeds`.`id` = `postings`.`feed_id` WHERE `feeds`.`type` = 1 AND `postings`.`user_offtopic_count` < 10 AND `postings`.`is_active` = 1 ORDER BY `postings`.`post_date` desc LIMIT 15 /* This should show REF access with lots of rows and no FILESORT */ 

MySQL有两种文件algorithm:一种是对磁盘上的logging进行sorting的旧文件,另一种是在内存中运行的新版本。

如果它不能使用联接中的第一个表上的索引对查询进行sorting,则必须执行一个文件夹。 如果在转换为固定宽度格式的sorting之前的结果集大于sorting缓冲区, 或者如果结果集包含任何文本字段,则必须使用较慢的磁盘上文件sortingalgorithm(第二个条件满足,因为您的查询具有文本字段) 。

MySQL正在select使用is_active列,表面上是因为它认为该列在消除行之前是最有select性的,因为在它继续与其他联接和条件。 我build议的第一件事就是尝试用post_date,feed_id和where条件中的列创build复合索引,例如(is_active,user_offtopic_count,post_date,feed_id)。

此外,重要的是要记住,如果您要sorting的列具有应用的函数,MySQL将不会使用索引。

你也应该尝试别名posts.post_date别名。 这将告诉MySQL通过未改变的列进行sorting,并且您仍将selectunix时间戳。