【问题标题】:MySQL performance issues using ORDER by使用 ORDER by 的 MySQL 性能问题
【发布时间】:2012-06-03 01:04:53
【问题描述】:

我遇到了性能问题,以下是在大约 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% 我会怎么做? ID Select_type表类型可能_keys键键键ref行额外 1   PRIMARY           所有    NULL        NULL    NULL    NULL           321使用文件排序 1   PRIMARY            ALL     NULL          NULL     NULL     NULL            3424   使用连接缓冲区 1主歌eq_ref主初级初级4 production1.song_id 1使用在哪里 3使用索引派生Song_Production Ref Migher Migher 4 339 2派生的song_genre index null主8 null 3424使用位置;使用索引

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 ;

【问题讨论】:

  • 派生表造成了损害。试试 MySQL 视图。
  • LIKE '%' ??你为什么有这个?为什么需要派生表?
  • @ypercube,派生表用于响应previous question - 另外,LIKE '%' 错误地留在了那里,但不影响性能。
  • @gowri 你能进一步解释一下吗?
  • @Sharpedges:链接显示不同的查询。请使用有意义的查询编辑您的问题。事实上,整个(第一个)派生表可以完全删除。如果您添加表的定义也很好。了解什么是主键和外键会有所帮助。

标签: mysql performance


【解决方案1】:

第一步:

我猜genre_idproduction_idapprovedinteger 列,而不是char,所以在'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 INT,而song_genre.song_idsong_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:

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

并测试哪个性能更快。

【讨论】:

    【解决方案2】:

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

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

    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 值的歌曲。

    【讨论】:

    • 感谢您的帮助,我同意示例中的子查询看起来是多余的,但在搜索多种流派/制作等时是必需的。
    【解决方案3】:

    好的,我只是重写查询:

        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
    

    首先,我将 order by 应用到在 index 中有 song_id 的表 song 上。我也希望优先。如果没有,请在此添加索引,并且 order by 不会通过临时表和文件排序。

    如果你没有为 order by 指定一个表,MySQL 可能会选择错误的表来进行 order by。

    【讨论】:

    • 一个好建议,我的列引用确实需要更正,但不幸的是,这两个问题都没有解决。
    • 我刚刚阅读了表定义,您没有关于 song.priority 的索引。添加它,然后运行查询,并给我解释它的查询很慢
    • 我只是没有更新原始问题。恐怕索引根本不会提高性能。
    猜你喜欢
    • 2016-03-10
    • 2014-06-11
    • 1970-01-01
    • 2019-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-17
    相关资源
    最近更新 更多