【问题标题】:MYSQL, very slow order byMYSQL,排序很慢
【发布时间】:2013-01-16 21:30:43
【问题描述】:

我有两张桌子。一个是用户表,其主键为用户 ID,另一个表引用用户表,外键。

User 表只有一个条目(目前),而另一个表有 100 万个条目。

下面的加入让我发疯:

 SELECT p0_.*, p1_.*
 FROM photo p0_, User p1_
 WHERE p0_.user_id = p1_.user_id
 ORDER BY p0_.uploaddate DESC Limit 10 OFFSET 100000

在一个非常快的机器上查询需要 12 秒,有 order by,0.0005 秒没有 order by。

我有一个关于 user_id (IDX_14B78418A76ED395) 的索引和一个关于 user_id 和 uploaddate 的复合索引 ("search2")。

EXPLAIN 显示如下:

+----+-------------+-------+------+------------------------------+----------------------+---------+---------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys                | key                  | key_len | ref                 | rows  | Extra                           |
+----+-------------+-------+------+------------------------------+----------------------+---------+---------------------+-------+---------------------------------+
|  1 | SIMPLE      | p1_   | ALL  | PRIMARY                      | NULL                 | NULL    | NULL                |     1 | Using temporary; Using filesort |
|  1 | SIMPLE      | p0_   | ref  | IDX_14B78418A76ED395,search2 | IDX_14B78418A76ED395 | 4       | odsfoto.p1_.user_id | 58520 |                                 |
+----+-------------+-------+------+------------------------------+----------------------+---------+---------------------+-------+---------------------------------+

表定义:

CREATE TABLE `photo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`album_id` int(11) DEFAULT NULL,
`exif_id` int(11) DEFAULT NULL,
`title` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`width` int(11) NOT NULL,
`height` int(11) NOT NULL,
`uploaddate` datetime NOT NULL,
`filesize` int(11) DEFAULT NULL,
`path` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`originalFilename` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`mimeType` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
`description` longtext COLLATE utf8_unicode_ci,
`gpsData_id` int(11) DEFAULT NULL,
`views` int(11) DEFAULT NULL,
`likes` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_14B78418B0FC9251` (`exif_id`),
UNIQUE KEY `UNIQ_14B7841867E96507` (`gpsData_id`),
KEY `IDX_14B78418A76ED395` (`user_id`),
KEY `IDX_14B784181137ABCF` (`album_id`),
KEY `search_idx` (`uploaddate`),
KEY `search2` (`user_id`,`uploaddate`),
KEY `search3` (`uploaddate`,`user_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


CREATE TABLE `user` (
`user_id` int(11) NOT NULL,
`photoCount` int(11) NOT NULL,
`photoViews` int(11) NOT NULL,
`photoComments` int(11) NOT NULL,
`photoLikes` int(11) NOT NULL,
`username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

我可以做些什么来加快这个查询?

【问题讨论】:

  • 单独在uploaddate 上建立索引,看看是否有帮助。
  • 有索引但没有被说明列出
  • 您能否提供p0_p1_ 的表定义以及sort_buffer_sizeread_rnd_buffer_size 的值
  • 简而言之,包括SHOW CREATE TABLE p1_SHOW CREATE TABLE p0_ 这样我们就可以看到如何创建表。
  • 我添加了表定义,在my.cnf中找不到这两个变量

标签: mysql


【解决方案1】:

您似乎正在为 MySQL 无法进行后期行查找而苦恼:

试试这个:

SELECT  p.*, u.*
FROM    (
        SELECT  id
        FROM    photo
        ORDER BY
                uploaddate DESC, id DESC
        LIMIT   10
        OFFSET  100000
        ) pi
JOIN    photo p
ON      p.id = pi.id
JOIN    user u
ON      u.user_id = p.user_id

【讨论】:

  • 谢谢,这要快得多,我明白了。但是为什么查询优化器不做这个简单的优化呢?
  • @user1985207:因为它没有被编程为这样做。查看链接。
【解决方案2】:

您需要在uploaddate 上建立单独的索引。只有当uploaddate 是其中的第一列时,这种排序才会利用复合索引。 您也可以尝试将 user_id 添加到 ORDER BY:

    ....      
    ORDER BY p0_.user_id, p0_.uploaddate

【讨论】:

  • uploaddate 上还有一个复合索引,user_id 但没有用到
  • 将 user_id 添加到 ORDER BY 没有任何区别
  • 如果在 ORDER BY 中交换 user_id 和 uploaddate 会怎样?
  • 执行时间没有区别
【解决方案3】:

你有两个问题:

  1. 您需要创建一个INDEX(user_id, uploaddate),这将大大提高查询的效率。

  2. 您需要找到使用LIMIT 10 OFFSET 100000 的解决方法。 MySQL 正在创建一个包含 100,000 条记录的记录集,然后它会将最后 10 条记录拉到最后……这是非常低效的。

https://www.percona.com/blog/2006/09/01/mysql-order-by-limit-performance-optimization/

【讨论】:

  • 如果我只使用limit并删除整个偏移量或将其设置为零也是一样
  • LIMIT 10LIMIT 10, 100000 在性能方面有很大不同。然而,您的查询速度慢的主要原因是缺少使用的索引。当您在查询上运行EXPLAIN 时,它应该使用索引并且永远不要使用ALL
  • 我知道,但是缺少什么索引。我认为使用“ALL”是因为用户中只有一个条目。
  • 链接地址已更改为:percona.com/blog/2006/09/01/…
【解决方案4】:

先尝试根据主键不加连接的方式获取结果,再使用结果查询结果。
例如:

$userIds=mysql::select("select user_id from photo ORDER BY p0_.uploaddate DESC Limit 10 OFFSET 100000");

$photoData=mysql::select("SELECT p0_., p1_. FROM 照片 p0_,用户 p1_ WHERE p0_.user_id = p1_.user_id 和 p0_.user_id in ($userIds->user_id) order by p0_.uploaddate");

在这里,我们将声明分为两部分:
1.我们可以很容易地根据主键订购和获取,而且没有连接。
2.根据id和order by获取查询结果只在有限的列上,我们可以在更短的时间内检索数据

【讨论】:

    【解决方案5】:

    使用Quassnoi 答案从 30 秒到 0.015 秒/0.000 秒! 这就是我所说的MySql专业知识! 我从我的个人项目中删除了一个 Join(不加入自身)

    Select ser.id_table, ser.id_rec, ser.relevance, cnt, title, description, sell_url, medium_thumb,  
            unique_id_supplier, keywords width, height, media_type 
    from (    
            Select ser.id_rec, ser.id_table, ser.relevance, ser.cnt 
            from searchEngineResults ser     
            where thisSearch = 16287     
        order by  ser.relevance desc, cnt desc, id_rec 
        ) ser 
    join photo_resell sou on sou.id = ser.id_rec 
    #join searchEngineResults ser on ser.id_rec = tmp.id_rec 
    limit 0, 9
    

    【讨论】:

      猜你喜欢
      • 2013-07-01
      • 1970-01-01
      • 2013-10-09
      • 2021-12-28
      • 1970-01-01
      • 1970-01-01
      • 2010-11-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多