按结果优化查询顺序到使用filesort;

时间:2012-03-16 09:35:46

标签: mysql sql indexing filesort

查询:

    SELECT
        r.reply_id,
        r.msg_id,
        r.uid,
        r.body,
        r.date,
        u.username as username,
        u.profile_picture as profile_picture
    FROM
        pm_replies as r
        LEFT JOIN users as u
            ON u.uid = r.uid
    WHERE
        r.msg_id = '784351921943772258'

    ORDER BY r.date DESC

我尝试了所有可以想到的索引组合,在谷歌中搜索我如何能够将其编入索引,但没有任何效果。

此查询在 500 返回的商品上 0,33 正在计算 ...


说明:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  r   ALL     index1  NULL    NULL    NULL    540     Using where; Using filesort
1   SIMPLE  u   eq_ref  uid     uid     8   site.r.uid  1   

SHOW CREATE pm_replies

CREATE TABLE `pm_replies` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `reply_id` bigint(20) NOT NULL,
 `msg_id` bigint(20) NOT NULL,
 `uid` bigint(20) NOT NULL,
 `body` text COLLATE utf8_unicode_ci NOT NULL,
 `date` datetime NOT NULL,
 PRIMARY KEY (`id`),
 KEY `index1` (`msg_id`,`date`,`uid`)
) ENGINE=MyISAM AUTO_INCREMENT=541 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

显示创建用户

CREATE TABLE `users` (
 `id` bigint(20) NOT NULL AUTO_INCREMENT,
 `uid` bigint(20) NOT NULL,
 `username` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
 `email` text CHARACTER SET latin1 NOT NULL,
 `password` text CHARACTER SET latin1 NOT NULL,
 `profile_picture` text COLLATE utf8_unicode_ci NOT NULL,
 `date_registered` datetime NOT NULL,
 PRIMARY KEY (`id`),
 UNIQUE KEY `uid` (`uid`),
 UNIQUE KEY `username` (`username`)
) ENGINE=MyISAM AUTO_INCREMENT=2004 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

5 个答案:

答案 0 :(得分:5)

对于查询原样,最佳索引似乎是......

pm_replies: (msg_id, date, uid)
users:      (uid)

重要的是pm_replies。您可以使用它来过滤数据(首先是过滤列)然后订购数据(订单栏是第二个)。

如果您删除了过滤器,则会有所不同。然后,您只需要(date, uid)作为索引。

索引中的最后一个字段使得它对连接更加友好,重要的部分实际上是users上的索引。

在这方面还有很多内容可以说,至少在一本书中有一章,如果你愿意,还有几本书。但我希望这会有所帮助。


修改

不是我建议的pm_replies索引是一个涵盖三个字段的索引,而不仅仅是三个索引。这可确保索引中的所有条目都按这些列进行预排序。这就像在Excel中按三列排序数据一样。

拥有三个独立的索引就像在三个选项卡上拥有Excel数据一样。每个按不同的字段排序。

只有三个字段中的一个索引才会出现此行为...
- 您可以使用相同的msg_id选择一组“记录” - 整个'束'彼此相邻,没有间隙等等 - 整个'束'按日期顺序排序为msg_id
- 对于具有相同日期的任何行,它们按user_id

排序

(同样,user_id部分非常小。)

答案 1 :(得分:0)

请试试这个:

SELECT
        r.reply_id,
        r.msg_id,
        r.uid,
        r.body,
        r.date,
        u.username as username,
        u.profile_picture as profile_picture
    FROM
        pm_replies as r
        LEFT JOIN users as u
            ON (u.uid = r.uid AND r.msg_id = '784351921943772258')
    ORDER BY r.date DESC

就我而言,它有所帮助。

答案 2 :(得分:0)

将日期添加到index1键,以便msg_id和date都在索引中。

答案 3 :(得分:0)

Dems is saying应该是正确的,但是如果您使用InnoDB还有一个额外的细节:也许您正在支付secondary indexes on clustered tables的价格 - 实际上,通过二级索引访问行需要附加查询主要,即群集索引。这种“双重查找”可能会使索引对查询优化器的吸引力降低。

要缓解此问题,请尝试使用索引{/ 3>

pm_replies: (msg_id, date, uid, reply_id, body, date)
users:      (uid, username, profile_picture)

答案 4 :(得分:0)

优化器似乎试图通过ID强制索引来连接到用户表。由于你正在做一个左连接(这是没有意义的,因为我希望每个条目都有一个用户ID,因此是一个正常的INNER JOIN),我会保持连接。

所以,我会尝试以下方法。只查询基于MESSAGE ID的回复,并根据自己的优点下降日期,然后左连接,例如

SELECT
        r.reply_id,
        r.msg_id,
        r.uid,
        r.body,
        r.date,
        u.username as username,
        u.profile_picture as profile_picture
    FROM
        ( select R2.* 
             from pm_replies R2
             where r2.msg_id = '784351921943772258' ) r
        LEFT JOIN users as u
            ON u.uid = r.uid
    ORDER BY
        r.date DESC

另外,由于我没有随时可用的MySQL,并且不记得子查询中是否允许使用order by,如果是,则可以优化内部预查询(使用别名“R2”)并放置由那里的顺序,所以它使用(msgid,date)索引并返回该集合... THEN连接到ID上的用户表,在该点上从SOURCE结果集中不需要索引,只是该索引用户表来查找匹配。