【问题标题】:Can I force 2 separate index on GROUP BY and ORDER BY?我可以在 GROUP BY 和 ORDER BY 上强制使用 2 个单独的索引吗?
【发布时间】:2015-06-04 06:58:21
【问题描述】:

我正在尝试按列分组,然后在 ORDER BY 另一列之后。它们都是索引,但除非我强迫它们,否则它们不会被使用。

这是我的查询:

SELECT a.* 
FROM
( 
    SELECT 
    id 
    FROM 
    articles temp FORCE INDEX (id_pub_date) 
    INNER JOIN 
    sources_articles sa FORCE INDEX (source_id) ON temp.id = sa.article_id 
    WHERE 
    sa.source_id IN (10,11,12,13,15,19,33,37,40,41,46) 
    GROUP BY id 
    ORDER BY pub_date DESC 
    LIMIT 0,10 
) id_array 
INNER JOIN
articles a ON a.id = id_array.id

这是我的数据库架构: article 大约有 160,000 行,sources_articles 大约有 200,000 行。

DROP TABLE IF EXISTS `articles`;
CREATE TABLE `articles` (
  `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `title` char(255) COLLATE utf8_unicode_ci NOT NULL,
  `img_url` char(255) CHARACTER SET latin1 NOT NULL,
  `vid_url` varchar(511) COLLATE utf8_unicode_ci NOT NULL,
  `vid_content_type` varchar(31) COLLATE utf8_unicode_ci NOT NULL,
  `vid_thumb_url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `description` text COLLATE utf8_unicode_ci NOT NULL,
  `url` varchar(511) CHARACTER SET latin1 NOT NULL,
  `date` int(10) unsigned NOT NULL,
  `pub_date` int(10) unsigned NOT NULL,
  `comment_url` char(255) CHARACTER SET latin1 NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `url` (`url`),
  KEY `id_pub_date` (`id`,`pub_date`),
  KEY `pub_date` (`pub_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;


DROP TABLE IF EXISTS `sources`;
CREATE TABLE `sources` (
  `s_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
  `s_name` char(255) CHARACTER SET latin1 NOT NULL,
  `s_short_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `s_slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `s_category` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `s_default` tinyint(1) NOT NULL,
  `s_active` tinyint(1) NOT NULL,
  PRIMARY KEY (`s_id`),
  KEY `s_slug` (`s_slug`),
  KEY `s_default` (`s_default`,`s_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of sources';


DROP TABLE IF EXISTS `sources_articles`;
CREATE TABLE `sources_articles` (
  `sa_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
  `source_id` tinyint(3) unsigned NOT NULL,
  `article_id` mediumint(8) unsigned NOT NULL,
  PRIMARY KEY (`sa_id`),
  KEY `source_id` (`source_id`),
  KEY `article_id` (`article_id`),
  CONSTRAINT `sources_articles_ibfk_1` FOREIGN KEY (`source_id`) REFERENCES `sources` (`s_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `sources_articles_ibfk_2` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

查询运行大约需要 600 毫秒。我正在使用由 idpub_date 列组成的索引。

如果我强制 id 作为 temp 的索引,那么 GROUP BYORDER BY id,查询大约需要 3ms

如果我强制 pub_date 作为索引,然后 GROUP BYORDER BY pub_date,则相同。 p>

我在想,如果我在分组时强制索引 id,并在排序时强制 pub_date 作为索引,它会运行得更快。据我了解,这在 mysql 上是不可能的。

当然,我可能看错了整个事情。

非常感谢任何提示或帮助 :)

编辑:

通过这个新的、更新的查询能够减少几百毫秒:

SELECT DISTINCT a.* 
FROM
( 
    SELECT
    id 
    FROM
    articles temp FORCE INDEX (id_pub_date) 
    INNER JOIN
    sources_articles sa FORCE INDEX (source_id_article_id) ON temp.id = sa.article_id 
    WHERE
    sa.source_id IN (10,11,12,13,15,19,33,37,40,41,46)  
    ORDER BY pub_date DESC 
    LIMIT 0,10 
) id_array 
INNER JOIN 
articles a ON a.id = id_array.id

【问题讨论】:

  • 你不能。 MySQL 将在一个语句空间内只使用一个索引(实际上,它可以只使用一个索引)
  • 删除 sa_id。删除 sa.source_id 和 sa.article_id 上的索引。而是将这两列用作您的 PK。如果您必须保留您的代理 PK,请至少将自然键设为 UNIQUE。
  • 另外,删除超级查询。选择 id 时只需选择 a.columns。毕竟是PK!!
  • @Strawberry - 超级查询的好处是子查询的 tmp 表只需要保留“id”,而不是整行。请注意,子查询有一个LIMIT,并且子查询可能是“使用索引”,这两者都放大了不拖着“a.*”的好处。

标签: mysql sql database performance join


【解决方案1】:

根据我描述的索引,我无法复制您的发现:

DROP TABLE IF EXISTS `articles`;
CREATE TABLE `articles` 
(article_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,pub_date DATE NOT NULL 
,INDEX(pub_date)
);

DROP TABLE IF EXISTS `sources`;
CREATE TABLE `sources` 
(source_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY);

DROP TABLE IF EXISTS `sources_articles`;
CREATE TABLE `sources_articles` 
(source_id INT NOT NULL
,article_id INT NOT NULL
,PRIMARY KEY (source_id,article_id)
);

SELECT COUNT(*) FROM articles;
+----------+
| COUNT(*) |
+----------+
|   131072 |
+----------+

SELECT COUNT(*) FROM sources;
+----------+
| COUNT(*) |
+----------+
|     1024 |
+----------+

SELECT COUNT(DISTINCT source_id),COUNT(DISTINCT article_id),COUNT(*) FROM sources_articles;
+---------------------------+----------------------------+----------+
| COUNT(DISTINCT source_id) | COUNT(DISTINCT article_id) | COUNT(*) |
+---------------------------+----------------------------+----------+
|                      1001 |                     112649 |   261748 |
+---------------------------+----------------------------+----------+


SELECT SQL_NO_CACHE DISTINCT a.*
                        FROM articles a
                        JOIN sources_articles sa 
                          ON sa.article_id = a.article_id
                       WHERE sa.source_id IN (10,11,12,13,15,19,33,37,40,41,46) 
                       ORDER 
                          BY a.pub_date DESC, a.article_id 
                       LIMIT 0,10;
+------------+------------+
| article_id | pub_date   |
+------------+------------+
|      72230 | 2015-06-23 |
|      90398 | 2015-06-21 |
|      72378 | 2015-06-21 |
|      87814 | 2015-06-20 |
|      66270 | 2015-06-19 |
|       8399 | 2015-06-19 |
|      21798 | 2015-06-18 |
|      95773 | 2015-06-16 |
|      67165 | 2015-06-15 |
|      19615 | 2015-06-14 |
+------------+------------+
10 rows in set (0.05 sec)

+----+-------------+-------+--------+---------------+---------+---------+---------------------+------+-----------------------------------------------------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                 | rows | Extra                                                     |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+------+-----------------------------------------------------------+
|  1 | SIMPLE      | sa    | range  | PRIMARY       | PRIMARY | 4       | NULL                | 2741 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | a     | eq_ref | PRIMARY       | PRIMARY | 4       | world.sa.article_id |    1 |                                                           |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+------+-----------------------------------------------------------+

你的查询也很快,但我的解释更漂亮:

SELECT a.*
  FROM
     ( SELECT temp.article_id
         FROM articles temp
         JOIN sources_articles sa 
           ON temp.article_id = sa.article_id
        WHERE sa.source_id IN (10,11,12,13,15,19,33,37,40,41,46)
        GROUP 
           BY article_id
        ORDER 
           BY pub_date DESC
        LIMIT 0,10
     ) id_array
  JOIN articles a 
    ON a.article_id = id_array.article_id;
+------------+------------+
| article_id | pub_date   |
+------------+------------+
|      72230 | 2015-06-23 |
|      90398 | 2015-06-21 |
|      72378 | 2015-06-21 |
|      87814 | 2015-06-20 |
|      66270 | 2015-06-19 |
|       8399 | 2015-06-19 |
|      21798 | 2015-06-18 |
|      95773 | 2015-06-16 |
|      67165 | 2015-06-15 |
|      19615 | 2015-06-14 |
+------------+------------+
10 rows in set (0.04 sec)

EXPLAIN
SELECT a.*
  FROM
     ( SELECT temp.article_id
         FROM articles temp
         JOIN sources_articles sa 
           ON temp.article_id = sa.article_id
        WHERE sa.source_id IN (10,11,12,13,15,19,33,37,40,41,46)
        GROUP 
           BY article_id
        ORDER 
           BY pub_date DESC
        LIMIT 0,10
     ) id_array
  JOIN articles a 
    ON a.article_id = id_array.article_id;
+----+-------------+------------+--------+---------------+---------+---------+---------------------+------+-----------------------------------------------------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref                 | rows | Extra                                                     |
+----+-------------+------------+--------+---------------+---------+---------+---------------------+------+-----------------------------------------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL    | NULL    | NULL                |   10 |                                                           |
|  1 | PRIMARY     | a          | eq_ref | PRIMARY       | PRIMARY | 4       | id_array.article_id |    1 |                                                           |
|  2 | DERIVED     | sa         | range  | PRIMARY       | PRIMARY | 4       | NULL                | 2741 | Using where; Using index; Using temporary; Using filesort |
|  2 | DERIVED     | temp       | eq_ref | PRIMARY       | PRIMARY | 4       | world.sa.article_id |    1 |                                                           |
+----+-------------+------------+--------+---------------+---------+---------+---------------------+------+-----------------------------------------------------------+

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-28
    • 2017-02-06
    相关资源
    最近更新 更多