首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MySQL性能优化:按日期时间字段排序

MySQL性能优化:按日期时间字段排序
EN

Stack Overflow用户
提问于 2009-04-03 17:47:29
回答 3查看 36.4K关注 0票数 38

我有一个有大约100.000篇博客帖子的表格,通过1:n关系链接到一个有50个提要的表格。当我使用select语句查询这两个表时(按postings表的日期时间字段排序),MySQL总是使用文件排序,这导致查询时间非常慢(>1秒)。以下是postings表的模式(简化):

代码语言:javascript
复制
+---------------------+--------------+------+-----+---------+----------------+
| 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表:

代码语言:javascript
复制
+-------------+--------------+------+-----+---------+----------------+
| 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没有使用它对postings表进行排序:

代码语言:javascript
复制
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正在使用文件排序:

代码语言:javascript
复制
+----+-------------+----------+--------+---------------------------------------+-----------+---------+--------------------------+-------+-----------------------------+
| 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部件时,MySQL停止使用文件排序。如果您对如何优化此查询以便让MySQL通过使用索引对数据进行排序和选择有任何想法,请让我知道。我已经尝试了一些方法,比如像一些博客文章所建议的那样,在所有的where/order by字段上创建一个组合索引,但这也不起作用。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2009-04-03 17:55:21

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

它将用于对is_active进行过滤和按post_date排序。

MySQL应在EXPLAIN EXTENDED中显示对此索引的REF访问方法。

请注意,您在user_offtopic_count上有一个RANGE过滤条件,这就是为什么您不能在过滤和按其他字段排序时对此字段使用索引。

根据user_offtopic_count的选择性(即有多少行满足user_offtopic_count < 10),在user_offtopic_count上创建索引并对post_dates进行排序可能更有用。

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

哪个索引更快取决于您的数据分布。创建这两个索引,对它们执行FORCE操作,看看哪一个更快:

代码语言:javascript
复制
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 */
票数 41
EN

Stack Overflow用户

发布于 2009-04-03 17:52:32

MySQL有两种文件排序算法:一种是在磁盘上对记录进行排序的旧文件排序算法,另一种是在内存中工作的新版本。

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

MySQL选择使用is_active列,表面上是因为它认为在继续其他连接和where条件之前,该列在消除行方面具有最大的选择性。我建议的第一件事是尝试使用post_date、feed_id和where条件中的列创建复合索引,例如(is_active、user_offtopic_count、post_date、feed_id)。

票数 3
EN

Stack Overflow用户

发布于 2009-08-25 05:41:53

此外,重要的是要记住,如果排序依据的列应用了函数,则MySQL不会使用索引。

您还应该尝试将postings.post_date的别名设置为其他名称。这将告诉MySQL按未更改的列排序,而您仍将选择unix时间戳。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/714950

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档