【问题标题】:Optimize MySql query: Too slow when ordering优化MySql查询:下单时太慢
【发布时间】:2011-10-24 14:34:06
【问题描述】:

(已编辑)有关应用程序本身的更多详细信息,请参阅: Simple but heavy application consuming a lot of resources. How to Optimize? (采用的解决方案是同时使用连接和全文搜索)

我在 25 秒内运行了大约 500.000 行以下查询。如果我删除订单,则需要 0.5 秒。

第一次测试

保留ORDER 并删除所有 t。和涂。列,查询需要 7 秒。

第二次测试

如果我在 i.created_at 字段中添加或删除索引,响应时间保持不变。

查询:

**已编辑:我注意到 GROUP BY 和 ORDER BY 都减慢了查询(我在更改连接的查询中也取得了一点收获。增益是 10 秒,但根本不是问题遗迹)。修改后,EXPLAIN 已停止返回文件排序,但仍返回“使用临时”**

SELECT SQL_NO_CACHE
        DISTINCT `i`.`id`, 
        `i`.`entity`, 
        `i`.`created_at`, 
        `i`.`collected_at`, 

        `t`.`status_id` AS  `twt_status_id`, 
        `t`.`user_id` AS `twt_user_id`, 
        `t`.`content` AS `twt_content`, 
        `tu`.`id` AS `twtu_id`, 
        `tu`.`screen_name` AS `twtu_screen_name`, 
        `tu`.`profile_image` AS `twtu_profile_image`


        FROM `mtrt_items` AS `i`

        LEFT JOIN `mtrt_users` AS `u` ON i.user_id =u.id

        LEFT JOIN `twt_tweets_content` AS `t` ON t.id =i.id
        LEFT JOIN `twt_users` AS `tu` ON u.id = tu.id

        INNER JOIN `mtrt_items_searches` AS `r` ON i.id =r.item_id
        INNER JOIN `mtrt_searches` AS `s` ON s.id =r.search_id
        INNER JOIN `mtrt_searches_groups` AS `sg` ON sg.search_id =s.id
        INNER JOIN `mtrt_search_groups` AS `g` ON sg.group_id =g.id
        INNER JOIN `account_clients` AS `c` ON g.client_id =c.id                

    ORDER BY `i`.`created_at` DESC 
    LIMIT 100 OFFSET 0

这是EXPLAIN (已编辑)

+----+-------------+-------+--------+--------------------+-----------+---------+------------------------+------+------------------------------+
| id | select_type | table | type   | possible_keys      | key       | key_len | ref                    | rows | Extra                        |
+----+-------------+-------+--------+--------------------+-----------+---------+------------------------+------+------------------------------+
|  1 | SIMPLE      | c     | index  | PRIMARY            | PRIMARY   | 4       | NULL                   |    1 | Using index; Using temporary |
|  1 | SIMPLE      | g     | ref    | PRIMARY,client_id  | client_id | 4       | clubr_new.c.id         |    3 | Using index                  |
|  1 | SIMPLE      | sg    | ref    | group_id,search_id | group_id  | 4       | clubr_new.g.id         |    1 | Using index                  |
|  1 | SIMPLE      | s     | eq_ref | PRIMARY            | PRIMARY   | 4       | clubr_new.sg.search_id |    1 | Using index                  |
|  1 | SIMPLE      | r     | ref    | search_id,item_id  | search_id | 4       | clubr_new.s.id         | 4359 | Using where                  |
|  1 | SIMPLE      | i     | eq_ref | PRIMARY            | PRIMARY   | 8       | clubr_new.r.item_id    |    1 |                              |
|  1 | SIMPLE      | u     | eq_ref | PRIMARY            | PRIMARY   | 8       | clubr_new.i.user_id    |    1 | Using index                  |
|  1 | SIMPLE      | t     | eq_ref | PRIMARY            | PRIMARY   | 4       | clubr_new.i.id         |    1 |                              |
|  1 | SIMPLE      | tu    | eq_ref | PRIMARY            | PRIMARY   | 8       | clubr_new.u.id         |    1 |                              |
+----+-------------+-------+--------+--------------------+-----------+---------+------------------------+------+------------------------------+

这是mtrt_items 表:

+--------------+-------------------------------------------------------+------+-----+---------+----------------+
| Field        | Type                                                  | Null | Key | Default | Extra          |
+--------------+-------------------------------------------------------+------+-----+---------+----------------+
| id           | bigint(20)                                            | NO   | PRI | NULL    | auto_increment |
| entity       | enum('twitter','facebook','youtube','flickr','orkut') | NO   | MUL | NULL    |                |
| user_id      | bigint(20)                                            | NO   | MUL | NULL    |                |
| created_at   | datetime                                              | NO   | MUL | NULL    |                |
| collected_at | datetime                                              | NO   |     | NULL    |                |
+--------------+-------------------------------------------------------+------+-----+---------+----------------+

 CREATE TABLE `mtrt_items` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `entity` enum('twitter','facebook','youtube','flickr','orkut') COLLATE utf8_unicode_ci NOT NULL,
  `user_id` bigint(20) NOT NULL,
  `created_at` datetime NOT NULL,
  `collected_at` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `mtrt_user_id` (`user_id`),
  KEY `entity` (`entity`),
  KEY `created_at` (`created_at`),
  CONSTRAINT `mtrt_items_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `mtrt_users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=309650 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

twt_tweets_content 是 MyISAM,也用于 fulltext 搜索:

+-----------+--------------+------+-----+---------+-------+
| Field     | Type         | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| id        | int(11)      | NO   | PRI | NULL    |       |
| user_id   | int(11)      | NO   | MUL | NULL    |       |
| status_id | varchar(100) | NO   | MUL | NULL    |       |
| content   | varchar(200) | NO   | MUL | NULL    |       |
+-----------+--------------+------+-----+---------+-------+

【问题讨论】:

  • 为了确定,您在每个基准测试之前都在执行 RESET QUERY CACHE。对吗?
  • 当你尝试ORDER BY i.id` DESC` 会发生什么?通常它会给你完全相同的结果。 id 是自动递增的,因此较旧的 id 意味着该记录是在过去创建的。
  • @spinning_plate 其实我用的是SELECT SQL_NO_CACHE...
  • @Sabeen-Malik 实际上是同一时间,这很奇怪。我注意到在 phpmyadmin 上使用 EXPLAIN,此问题中发布的结果的第一行具有以下内容:Using index; Using temporary; Using filesort 不仅仅是using index。你知道为什么吗?
  • 愚蠢的想法,但如果你把整个东西包装在 SELECT * FROM (your query without ordering) alias ORDER BY alias.id 中,你能得到 0.05 秒的运行时间吗?

标签: mysql sql performance sql-order-by


【解决方案1】:

不要将Order By 放入主查询中,而是将其包装起来,如下所示:

SELECT * FROM (   
  ... your query
) ORDER BY `created at`

查看查询计划。您会发现,在您的情况下,排序是在执行外连接之前在您的表 mtrt_items 上执行的。在我部分提供的重写中,排序在外连接之后应用,并应用于更小的集合。

更新

假设 LIMIT 应用于一个大集合(500,000?),看起来您可以在执行任何连接之前执行顶部操作。

SELECT * from (
    SELECT 
    `id`, ... `created_at`, ...
    ORDER BY `i`.`created_at` DESC 
    LIMIT 100 OFFSET 0) as i

    LEFT JOIN `mtrt_users` AS `u` ON i.user_id =u.id

    LEFT JOIN `twt_tweets_content` AS `t` ON t.id =i.id
    LEFT JOIN `twt_users` AS `tu` ON t.user_id = tu.id

    INNER JOIN `mtrt_items_searches` AS `r` ON i.id =r.item_id
    INNER JOIN `mtrt_searches` AS `s` ON s.id =r.search_id
    INNER JOIN `mtrt_searches_groups` AS `sg` ON sg.search_id =s.id
    INNER JOIN `mtrt_search_groups` AS `g` ON sg.group_id =g.id
    INNER JOIN `account_clients` AS `c` ON g.client_id =c.id                

GROUP BY i.id

【讨论】:

  • 正如我在另一条评论中所说的......同时。
  • 另外,检查说明返回“使用索引;使用临时;使用文件排序”。也许是问题?
  • 所有文件排序意味着它正在排序。这是一个坏名字。它实际上是一种快速排序。令人恼火的是,如果如您所说,那么排序确实在对一个巨大的集合进行排序,它只会返回 100 个项目。如果它从中提取 100 个项目的表是,例如,500,000 个项目,那么这就是为什么一切都会慢下来。
  • 不确定我是否理解你的意思。但在这种情况下,我得到了大约 300k 个项目和大约 150k 用户的结果,总计大约 450k。
  • 不幸的是,大集来自一个测试账户,它会有多个,每个月大约有 ~100k+。然后,大集合将在一个学期内达到约 500 万+。
【解决方案2】:

不要在初始查询中包含 VARCHAR/TEXT 字段。这将使用 MEMORY 引擎创建排序所需的 TEMPORARY 表,这将显着提高效率。您可以稍后使用另一个查询收集文本字段,无需任何排序,只需在 PRIMARY KEY 字段上添加一个条件,然后合并脚本中的数据(假设您正在使用一个)。
还要删除您实际上并未从中获取任何数据的任何 JOIN(INNER 或 OUTER)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-28
    • 2016-11-30
    • 2021-06-23
    • 2014-01-27
    • 2013-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多