慢速加入订单查询

Slow join with order query

我查询速度有问题。问题类似于this one,但找不到解决方案。解释说 MySQL 正在使用: Using where;使用索引;使用临时的;使用文件排序 慢查询:

select 
    distinct(`books`.`id`)
from `books`
join `books_genres` on `books_genres`.`book_id` = `books`.`id`
where 
    `books`.`is_status` = 'active' and `books`.`master_book` = 'true'  
    and `books_genres`.`genre_id` in(380,381,384,385,1359)
order by 
    `books`.`livelib_read_num` DESC, `books`.`id` DESC
limit 0,25
#25 rows (0.319 s)

但是如果我从查询中删除订单语句,它真的很快:

select sql_no_cache
    distinct(`books`.`id`)
from `books`
join `books_genres` on `books_genres`.`book_id` = `books`.`id`
where 
    `books`.`is_status` = 'active' and `books`.`master_book` = 'true'  
    and `books_genres`.`genre_id` in(380,381,384,385,1359)
limit 0,25
#25 rows (0.005 s)

解释:

   +------+-------------+--------------+--------+---------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------------------+--------+-----------------------------------------------------------+
| id   | select_type | table        | type   | possible_keys                                                                                                       | key              | key_len | ref                            | rows   | Extra                                                     |
+------+-------------+--------------+--------+---------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------------------+--------+-----------------------------------------------------------+
|    1 | SIMPLE      | books_genres | range  | book_id,categorie_id,book_id2,genre_id_book_id                                                                      | genre_id_book_id | 10      | NULL                           | 194890 | Using where; Using index; Using temporary; Using filesort |
|    1 | SIMPLE      | books        | eq_ref | PRIMARY,is_status,master_book,is_status_master_book,is_status_master_book_indexed,is_status_donor_no_ru_master_book | PRIMARY          | 4       | knigogid3.books_genres.book_id |      1 | Using where                                               |
+------+-------------+--------------+--------+---------------------------------------------------------------------------------------------------------------------+------------------+---------+--------------------------------+--------+-----------------------------------------------------------+
2 rows in set (0.00 sec)

我的桌子:

CREATE TABLE `books_genres` (
  `book_id` int(11) DEFAULT NULL,
  `genre_id` int(11) DEFAULT NULL,
  `sort` tinyint(4) DEFAULT NULL,
  UNIQUE KEY `book_id` (`book_id`,`genre_id`),
  KEY `categorie_id` (`genre_id`),
  KEY `sort` (`sort`),
  KEY `book_id2` (`book_id`),
  KEY `genre_id_book_id` (`genre_id`,`book_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `books` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `is_status` enum('active','parser','incorrect','extremist','delete','fulldeteled') NOT NULL DEFAULT 'active',
  `livelib_book_id` int(11) DEFAULT NULL,
  `master_book` enum('true','false') DEFAULT 'true'
  PRIMARY KEY (`id`),
  KEY `is_status` (`is_status`),
  KEY `master_book` (`master_book`),
  KEY `livelib_book_id` (`livelib_book_id`),
  KEY `livelib_read_num` (`livelib_read_num`),
  KEY `is_status_master_book` (`is_status`,`master_book`),
  KEY `livelib_book_id_master_book` (`livelib_book_id`,`master_book`),
  KEY `is_status_master_book_indexed` (`is_status`,`master_book`,`indexed`),
  KEY `is_status_donor_no_ru_master_book` (`is_status`,`donor`,`no_ru`,`master_book`),
  KEY `livelib_url_master_book_is_status` (`livelib_url`,`master_book`,`is_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

books_genres 的问题。

  • 没有PRIMARY KEY.
  • 所有列都可以为空。你会用 any NULLs 插入一行吗?

推荐(在所有列上说 NOT NULL 之后):

PRIMARY KEY(`book_id`,`genre_id`)
INDEX(genre_id, book_id, sort)

并删除所有其余部分。

我在 table 中没有看到 livelib_read_num???

在另一个 table 中,删除所有与其他索引完全相同的前缀。

这些可能有助于提高速度。 (同样,过滤掉多余的前缀索引。)(这些是“覆盖”索引,这有点帮助。)

books:  INDEX(is_status, master_book, livelib_read_num, id)
books:  INDEX(livelib_read_num, id, is_status, master_book)

第二个索引 可能 导致优化器优先考虑 ORDER BY。 (这是一个有风险的优化,因为它可能必须扫描整个索引而找不到 25 个相关行。)

SELECT sql_no_cache
       `books`.`id`
    FROM
        `books` 
    use index(books_idx_is_stat_master_livelib_id)
    WHERE
        (
            1 = 1 
            AND `books`.`is_status` = 'active' 
            AND `books`.`master_book` = 'true'
        ) 
        AND (
            EXISTS (
                SELECT
                    1 
                FROM
                    `books_genres` 
                WHERE
                    (
                        `books_genres`.`book_id` = `books`.`id`
                    ) 
                    AND (
                        `books_genres`.`genre_id` IN (
                            380, 381, 384, 385, 1359
                        )
                    )
            )
        ) 
    ORDER BY
        `books`.`livelib_read_num` DESC,
        `books`.`id` DESC LIMIT 0,
        25;
25 rows in set (0.07 sec)