8
MySQL Server version: 5.0.95
Tables All: InnoDB

MySQL db クエリに問題があります。基本的に、特定の varchar(50) フィールドtag.nameにインデックスを付けると、フィールドにインデックスを付けないよりもクエリに時間がかかる (x10) ことがわかりました。このクエリを高速化しようとしていますが、私の努力は逆効果のようです。

犯人の行とフィールドは次のようです。

WHERE `t`.`name` IN ('news','home')

tag同じ条件を使用し、名前フィールドにインデックスを付けて、結合せずにテーブルを直接クエリすると、問題がないことに気付きました.実際には、期待どおりに高速に動作します。

例 クエリ **

      SELECT `a`.*, `u`.`pen_name`
        FROM `tag_link` `tl`
  INNER JOIN `tag` `t`
          ON `t`.`tag_id` = `tl`.`tag_id`
  INNER JOIN `article` `a` 
          ON `a`.`article_id` = `tl`.`link_id`
  INNER JOIN `user` `u`
          ON `a`.`user_id` = `u`.`user_id`   
       WHERE `t`.`name` IN ('news','home')
         AND `tl`.`type` = 'article'
         AND `a`.`featured` = 'featured'
    GROUP BY `article_id`
       LIMIT 0 , 5

EXPLAIN with index **

| id | select_type | table | type   | possible_keys            | key     | key_len | ref               | rows | Extra                                                     |
+----+-------------+-------+--------+--------------------------+---------+---------+-------------------+------+-----------------------------------------------------------+
|  1 | SIMPLE      | t     | range  | PRIMARY,name             | name    | 152     | NULL              |    4 | Using where; Using index; Using temporary; Using filesort | 
|  1 | SIMPLE      | tl    | ref    | tag_id,link_id,link_id_2 | tag_id  | 4       | portal.t.tag_id   |   10 | Using where                                               | 
|  1 | SIMPLE      | a     | eq_ref | PRIMARY,fk_article_user1 | PRIMARY | 4       | portal.tl.link_id |    1 | Using where                                               | 
|  1 | SIMPLE      | u     | eq_ref | PRIMARY                  | PRIMARY | 4       | portal.a.user_id  |    1 |                                                           | 
+----+-------------+-------+--------+--------------------------+---------+---------+-------------------+------+-----------------------------------------------------------+

インデックスなしで EXPLAIN **

+----+-------------+-------+--------+--------------------------+---------+---------+---------------------+------+-------------+
| id | select_type | table | type   | possible_keys            | key     | key_len | ref                 | rows | Extra       |
+----+-------------+-------+--------+--------------------------+---------+---------+---------------------+------+-------------+
|  1 | SIMPLE      | a     | index  | PRIMARY,fk_article_user1 | PRIMARY | 4       | NULL                | 8742 | Using where | 
|  1 | SIMPLE      | u     | eq_ref | PRIMARY                  | PRIMARY | 4       | portal.a.user_id    |    1 |             | 
|  1 | SIMPLE      | tl    | ref    | tag_id,link_id,link_id_2 | link_id | 4       | portal.a.article_id |    3 | Using where | 
|  1 | SIMPLE      | t     | eq_ref | PRIMARY                  | PRIMARY | 4       | portal.tl.tag_id    |    1 | Using where | 
+----+-------------+-------+--------+--------------------------+---------+---------+---------------------+------+-------------+

テーブル作成

CREATE TABLE `tag` (
  `tag_id` int(11) NOT NULL auto_increment,
  `name` varchar(50) NOT NULL,
  `type` enum('layout','image') NOT NULL,
  `create_dttm` datetime default NULL,
  PRIMARY KEY  (`tag_id`)
) ENGINE=InnoDB AUTO_INCREMENT=43077 DEFAULT CHARSET=utf8 

インデックス

SHOW INDEX FROM tag_link;
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table    | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| tag_link |          0 | PRIMARY  |            1 | tag_link_id | A         |       42023 |     NULL | NULL   |      | BTREE      |         |
| tag_link |          1 | tag_id   |            1 | tag_id      | A         |       10505 |     NULL | NULL   |      | BTREE      |         |
| tag_link |          1 | link_id  |            1 | link_id     | A         |       14007 |     NULL | NULL   |      | BTREE      |         |
+----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

SHOW INDEX FROM article;
+---------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table   | Non_unique | Key_name         | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| article |          0 | PRIMARY          |            1 | article_id  | A         |        5723 |     NULL | NULL   |      | BTREE      |         |
| article |          1 | fk_article_user1 |            1 | user_id     | A         |           1 |     NULL | NULL   |      | BTREE      |         |
| article |          1 | create_dttm      |            1 | create_dttm | A         |        5723 |     NULL | NULL   | YES  | BTREE      |         |
+---------+------------+------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

最終的な解決策 MySQL がデータを正しくソートしていないようです。最終的に、ID を返すサブクエリとしてタグ テーブルを参照する方が高速であることが判明しました。

4

2 に答える 2

4

article_id が article テーブルの主キーのようです。

article_id でグループ化しているため、GROUP BY を実行するために、MySQL はその列の順にレコードを返す必要があります。

インデックスがないと、article テーブル内のすべてのレコードがスキャンされることがわかりますが、少なくとも article_id 順になっているため、後で並べ替える必要はありません。ここで LIMIT 最適化を適用することができます。これは既に順序付けられているため、5 行を取得したら停止できます。

tag.name のインデックスを使用したクエリでは、article テーブル全体をスキャンする代わりに、インデックスを使用しますが、tag テーブルに対して、そこから開始します。残念ながら、これを行う場合、GROUP BY 句を完成させるために、後でレコードを article.article_id でソートする必要があります。LIMIT 最適化は適用できません。最初の 5 行を取得するには、結果セット全体を返してから並べ替える必要があるためです。

この場合、MySQL は単に推測を誤っています。

LIMIT 句がなければ、インデックスを使用する方が高速であると推測しています。これは、おそらく MySQL が推測していたことです。

于 2012-06-07T13:39:16.090 に答える
1

あなたのテーブルはどれくらいの大きさですか?最初の説明で、「一時的な使用;ファイルソートの使用」が悪いことに気づきました。クエリがディスクにダンプされる可能性があり、メモリクエリよりもはるかに遅くなります。また、「select *」の使用を避け、代わりに必要な最小限のフィールドをクエリするようにしてください。

于 2012-06-07T12:58:27.483 に答える