使用INNER JOIN时MySQL不响应简单查询.特别是"发送数据"需要花费太多时间

Cha*_*kka 11 mysql sql inner-join

我有两张桌子; songs48 959 281行,popular_songs5 721 117行.

这是两个表的结构:

CREATE TABLE songs (
  songId       BIGINT(20)    NOT NULL,
  songName     VARCHAR(1000) NOT NULL,
  songDuration BIGINT(20)    NOT NULL,
  songPreview  VARCHAR(1000) NOT NULL,
  PRIMARY KEY (songId),
  INDEX (songDuration)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8
  DEFAULT COLLATE utf8_general_ci;

CREATE TABLE popular_songs (
  storeFrontId BIGINT(20) NOT NULL,
  genreId      BIGINT(20) NOT NULL,
  songId       BIGINT(20) NOT NULL,
  songRank     INT(11)    NOT NULL,
  INDEX (storeFrontId),
  INDEX (genreId),
  INDEX (songId),
  INDEX (songRank),
  CONSTRAINT popular_song UNIQUE (storeFrontId, genreId, songId),
  FOREIGN KEY (storeFrontId) REFERENCES storefront (storeFrontId),
  FOREIGN KEY (genreId) REFERENCES genre (genreId),
  FOREIGN KEY (songId) REFERENCES songs (songId)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8
  DEFAULT COLLATE utf8_general_ci;
Run Code Online (Sandbox Code Playgroud)

当我在下面运行时,两个quires一切正常.

mysql> SELECT count(*) FROM songs;
+----------+
| count(*) |
+----------+
| 48959281 |
+----------+
1 row in set (9.10 sec)

mysql> SHOW PROFILE;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000008 |
| Waiting for query cache lock   | 0.000002 |
| checking query cache for query | 0.000019 |
| checking permissions           | 0.000003 |
| Opening tables                 | 0.000011 |
| System lock                    | 0.000004 |
| Waiting for query cache lock   | 0.000017 |
| init                           | 0.000006 |
| optimizing                     | 0.000003 |
| statistics                     | 0.000004 |
| preparing                      | 0.000004 |
| executing                      | 0.000003 |
| Sending data                   | 9.100444 |
| end                            | 0.000012 |
| query end                      | 0.000005 |
| closing tables                 | 0.000008 |
| freeing items                  | 0.000007 |
| Waiting for query cache lock   | 0.000002 |
| freeing items                  | 0.000012 |
| Waiting for query cache lock   | 0.000002 |
| freeing items                  | 0.000001 |
| storing result in query cache  | 0.000002 |
| logging slow query             | 0.000001 |
| cleaning up                    | 0.000003 |
+--------------------------------+----------+
24 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
mysql> SELECT count(*) FROM popular_songs;
+----------+
| count(*) |
+----------+
|  5721117 |
+----------+
1 row in set (1.34 sec)

mysql> SHOW PROFILE;
+--------------------------------+----------+
| Status                         | Duration |
+--------------------------------+----------+
| starting                       | 0.000012 |
| Waiting for query cache lock   | 0.000004 |
| checking query cache for query | 0.000037 |
| checking permissions           | 0.000006 |
| Opening tables                 | 0.000017 |
| System lock                    | 0.000007 |
| Waiting for query cache lock   | 0.000031 |
| init                           | 0.000010 |
| optimizing                     | 0.000017 |
| statistics                     | 0.000004 |
| preparing                      | 0.000004 |
| executing                      | 0.000003 |
| Sending data                   | 1.343991 |
| end                            | 0.000010 |
| query end                      | 0.000005 |
| closing tables                 | 0.000007 |
| freeing items                  | 0.000007 |
| Waiting for query cache lock   | 0.000002 |
| freeing items                  | 0.000011 |
| Waiting for query cache lock   | 0.000001 |
| freeing items                  | 0.000001 |
| storing result in query cache  | 0.000002 |
| logging slow query             | 0.000001 |
| cleaning up                    | 0.000003 |
+--------------------------------+----------+
24 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

当我在运行以下查询时,MySQL没有响应.我不明白是什么原因.

SELECT COUNT(*)
FROM songs
INNER JOIN popular_songs ON popular_songs.songId = songs.songId
Run Code Online (Sandbox Code Playgroud)
SELECT COUNT(*)
FROM songs
INNER JOIN popular_songs ON popular_songs.songId = songs.songId
WHERE songs.songDuration > 0
Run Code Online (Sandbox Code Playgroud)

更新:

MySQL回复了以上两个查询.但它需要250-300秒Sending data.如何优化这一点.

EXPLAIN EXTENDED报告:

mysql> EXPLAIN EXTENDED
    -> SELECT COUNT(*)
    -> FROM songs
    -> INNER JOIN popular_songs ON popular_songs.songId = songs.songId;
+----+-------------+---------------+--------+---------------+----------+---------+-----------------------------+---------+----------+-------------+
| id | select_type | table         | type   | possible_keys | key      | key_len | ref                         | rows    | filtered | Extra       |
+----+-------------+---------------+--------+---------------+----------+---------+-----------------------------+---------+----------+-------------+
|  1 | SIMPLE      | popular_songs | index  | songId        | songRank | 4       | NULL                        | 6449163 |   100.00 | Using index |
|  1 | SIMPLE      | songs         | eq_ref | PRIMARY       | PRIMARY  | 8       | itunes.popular_songs.songId |       1 |   100.00 | Using index |
+----+-------------+---------------+--------+---------------+----------+---------+-----------------------------+---------+----------+-------------+
2 rows in set, 1 warning (0.01 sec)

mysql> EXPLAIN EXTENDED
    -> SELECT COUNT(*)
    -> FROM songs
    -> INNER JOIN popular_songs ON popular_songs.songId = songs.songId
    -> WHERE songs.songDuration > 0;
+----+-------------+---------------+--------+----------------------+----------+---------+-----------------------------+---------+----------+-------------+
| id | select_type | table         | type   | possible_keys        | key      | key_len | ref                         | rows    | filtered | Extra       |
+----+-------------+---------------+--------+----------------------+----------+---------+-----------------------------+---------+----------+-------------+
|  1 | SIMPLE      | popular_songs | index  | songId               | songRank | 4       | NULL                        | 6449163 |   100.00 | Using index |
|  1 | SIMPLE      | songs         | eq_ref | PRIMARY,songDuration | PRIMARY  | 8       | itunes.popular_songs.songId |       1 |   100.00 | Using where |
+----+-------------+---------------+--------+----------------------+----------+---------+-----------------------------+---------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

使用INDEX songId连接两个表时:

mysql> SELECT COUNT(*) FROM songs INNER JOIN popular_songs USE INDEX FOR JOIN (songId) ON popular_songs.songId = songs.songId;
+----------+
| COUNT(*) |
+----------+
|  5721117 |
+----------+
1 row in set (25.35 sec)

mysql> SHOW PROFILE;
+--------------------------------+-----------+
| Status                         | Duration  |
+--------------------------------+-----------+
| starting                       |  0.000011 |
| Waiting for query cache lock   |  0.000004 |
| checking query cache for query |  0.000045 |
| checking permissions           |  0.000004 |
| checking permissions           |  0.000004 |
| Opening tables                 |  0.000023 |
| System lock                    |  0.000007 |
| Waiting for query cache lock   |  0.000030 |
| init                           |  0.000021 |
| optimizing                     |  0.000011 |
| statistics                     |  0.000020 |
| preparing                      |  0.000012 |
| executing                      |  0.000005 |
| Sending data                   | 25.350160 |
| end                            |  0.000019 |
| query end                      |  0.000005 |
| closing tables                 |  0.000024 |
| freeing items                  |  0.000022 |
| Waiting for query cache lock   |  0.000002 |
| freeing items                  |  0.000016 |
| Waiting for query cache lock   |  0.000001 |
| freeing items                  |  0.000001 |
| storing result in query cache  |  0.000002 |
| logging slow query             |  0.000001 |
| logging slow query             |  0.000005 |
| cleaning up                    |  0.000003 |
+--------------------------------+-----------+
26 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

这很不错.但是,它还需要25秒Sending data.

解释以上查询的扩展:

mysql> EXPLAIN EXTENDED SELECT COUNT(*) FROM songs INNER JOIN popular_songs USE INDEX FOR JOIN (songId) ON popular_songs.songId = songs.songId;
+----+-------------+---------------+--------+---------------+---------+---------+-----------------------------+---------+----------+-------------+
| id | select_type | table         | type   | possible_keys | key     | key_len | ref                         | rows    | filtered | Extra       |
+----+-------------+---------------+--------+---------------+---------+---------+-----------------------------+---------+----------+-------------+
|  1 | SIMPLE      | popular_songs | index  | songId        | songId  | 8       | NULL                        | 2684407 |   100.00 | Using index |
|  1 | SIMPLE      | songs         | eq_ref | PRIMARY       | PRIMARY | 8       | itunes.popular_songs.songId |       1 |   100.00 | Using index |
+----+-------------+---------------+--------+---------------+---------+---------+-----------------------------+---------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

Gor*_*off 5

我猜测索引songDuration会让优化器混乱.您可以尝试像这样填写查询:

SELECT COUNT(*)
FROM songs s
WHERE EXISTS (SELECT 1
              FROM popular_songs ps
              WHERE ps.songId = s.songId
             ) AND
      s.songDuration > 0;
Run Code Online (Sandbox Code Playgroud)

指数songDuration也会更好地包括在内songId: songs(songDuration, songId).