使用ORDER by的MySQL性能问题

时间:2012-05-28 17:33:26

标签: mysql performance

我遇到了性能问题,因此以下操作在大约2.5秒内执行,只返回40行:

SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig 
FROM song 
INNER JOIN (
    SELECT song_id 
    FROM song_genre 
    WHERE genre_id IN ('25')
) genre1 ON genre1.song_id = song.song_id 
INNER JOIN (
    SELECT song_id 
    FROM song_production 
    WHERE production_id IN ('8')
) production1 ON production1.song_id = song.song_id 
WHERE approved='1' 
ORDER by song.priority DESC, song.song_id DESC 
LIMIT 0, 40

运行查询,丢弃ORDER BY,执行时间为0.01秒等。

我理解这个问题可能与信息的计数方式有关,因为我正在使用JOINS,所以可能需要嵌套查询,但我不是100%我会怎么做呢? / p>

id   select_type   table             type     possible_keys   key       key_len   ref                   rows   Extra
 1   PRIMARY       <derived3>        ALL      NULL            NULL      NULL      NULL                   321   Using temporary; Using filesort
 1   PRIMARY       <derived2>        ALL      NULL            NULL      NULL      NULL                  3424   Using join buffer
 1   PRIMARY       song              eq_ref   PRIMARY         PRIMARY   4         production1.song_id      1   Using where
 3   DERIVED       song_production   ref      PRIMARY         PRIMARY   4                                339   Using index
 2   DERIVED       song_genre        index    NULL            PRIMARY   8         NULL                  3424   Using where; Using index

song

CREATE TABLE `song` (
`song_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` tinytext,
`length` varchar(5) DEFAULT NULL,
`Type` varchar(1) DEFAULT NULL,
`Vocals` varchar(10) DEFAULT NULL,
`Themes` varchar(10) DEFAULT NULL,
`Explicit` varchar(10) DEFAULT NULL,
`timesig` varchar(3) DEFAULT NULL,
`keysig` varchar(250) NOT NULL,
`bpm` int(3) DEFAULT NULL,
`speed` varchar(7) DEFAULT NULL,
`Era` varchar(10) DEFAULT NULL,
`Language` varchar(10) DEFAULT NULL,
`Keywords` varchar(10) DEFAULT NULL,
`description` mediumtext,
`search_description` longtext NOT NULL,
`key` varchar(25) NOT NULL,
`priority` int(2) NOT NULL,
`approved` int(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`song_id`),
FULLTEXT KEY `description` (`description`),
FULLTEXT KEY `search_description` (`search_description`),
FULLTEXT KEY `title` (`title`),
FULLTEXT KEY `head_desc` (`title`,`search_description`)
) ENGINE=MyISAM 
  AUTO_INCREMENT=1388 
  DEFAULT CHARSET=utf8 ;

song_genre

CREATE TABLE `song_genre` (
`genre_id` int(10) NOT NULL,
`song_id` int(10) NOT NULL, 
PRIMARY KEY (`genre_id`,`song_id`)
) ENGINE=MyISAM 
  DEFAULT CHARSET=latin1 ;

song_production

CREATE TABLE `song_production` (
`production_id` int(10) NOT NULL,
`song_id` int(10) NOT NULL,
PRIMARY KEY (`production_id`,`song_id`)
) ENGINE=MyISAM 
  DEFAULT CHARSET=latin1 ;

3 个答案:

答案 0 :(得分:2)

第一步:

我认为genre_idproduction_idapprovedinteger列,而不是char,所以在{{{{{{ 1}}。取消引用它们:'25'

如果删除25会怎样? (没有表的结构,主要,外键和唯一约束,我们无法知道)。你在结果中得到多个相同的行吗?如果不是,请删除DISTINCT。如果是,请将其删除并添加DISTINCT

GROUP BY song.song_id

第二步:

添加有用的索引。 SELECT song.song_id, song.title, song.length, song.bpm, song.keysig FROM song INNER JOIN ( SELECT song_id FROM song_genre WHERE genre_id IN (25) ) genre1 ON genre1.song_id = song.song_id INNER JOIN ( SELECT song_id FROM song_production WHERE production_id IN (8) ) production1 ON production1.song_id = song.song_id WHERE approved = 1 ----- GROUP BY song.song_id --- not needed at all (with these tables) --- (and structure) ORDER BY song.priority DESC, song.song_id DESC LIMIT 0, 40 ; 上的索引可能有助于查询。

(approved, priority, song_id)列定义为song.song_id,而UNSIGNED INTsong_genre.song_id定义为song_production.song_id。如果您将它们转换为SIGNED INT也会很好。

我还会在UNSIGNED INT(song_id, genre_id)上添加(唯一)索引。它们可能对此查询没有用,但在其他情况下你肯定需要这些索引。


第三步:

尝试以其他方式重写查询。没有派生表,例如:

(song_id, production_id)

SELECT song.song_id, song.title, song.length, song.bpm, song.keysig FROM song INNER JOIN song_genre AS genre1 ON genre1.song_id = song.song_id INNER JOIN song_production AS production1 ON production1.song_id = song.song_id WHERE song.approved = 1 AND genre1.genre_id IN (25) AND production1.production_id IN (8) ORDER BY song.priority DESC , song.song_id DESC LIMIT 0, 40 ;

EXISTS

并测试哪一个表现得更快。

答案 1 :(得分:0)

MySQL会在应用限制之前对所有行运行ORDER BY,因此如果您的song表很大,并且没有正确编入索引,那么它仍然会很慢。您可以使用post on MySQL Performance Blog一些方法来加速ORDER BY ... LIMIT次查询。

我会注意到子查询有点多余,连接会处理这个问题。你可以像这样重写这个查询:

SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig 
FROM song 
JOIN song_genre g
  ON g.song_id = song.song_id 
JOIN song_production p
  ON p.song_id = song.song_id 
WHERE approved='1' 
  AND g.genre_id IN ('25')
  AND p.production_id IN ('8')
ORDER by priority DESC, song_id DESC 
LIMIT 0, 40

我甚至不确定你是否需要SELECT上的DISTINCT,除非你有多首歌曲具有完全相同的id / title / length / bpm / keysig值。

答案 2 :(得分:-1)

好的,我只是重写了查询:

    SELECT DISTINCT song.song_id, song.title, song.length, song.bpm, song.keysig
FROM song
    INNER JOIN (
        SELECT song_id
        FROM song_genre
        WHERE genre_id LIKE '%'
    ) genre1
        ON genre1.song_id = song.song_id
    INNER JOIN (
        SELECT song_id
        FROM song_production
        WHERE production_id IN ('5')
    ) production1
        ON production1.song_id = song.song_id
WHERE approved='1'
ORDER by song.priority DESC, song.song_id DESC
LIMIT 0, 40

首先,我在桌面歌曲上应用顺序,歌曲在索引中有song_id。我也希望优先考虑。如果没有,请在此处添加索引,并且order by不会通过临时表和filesort传递。

如果您没有为订单提供特定的表,MySQL可以选择错误的表来执行订单。