9

このクエリを最適化しようとしています:

SELECT `posts`.* FROM `posts` INNER JOIN `posts_tags`
     ON `posts`.id = `posts_tags`.post_id
     WHERE (((`posts_tags`.tag_id = 1)))
     ORDER BY posts.created_at DESC;

テーブルのサイズは 38k 行で、31k と mysql は "filesort" を使用するため、かなり遅くなります。別のインデックスを使用しようとしましたが、うまくいきませんでした。

CREATE TABLE `投稿` (
  `id` int(11) NOT NULL auto_increment,
  `created_at`日時デフォルトNULL、
  主キー (`id`)、
  KEY `index_posts_on_created_at` (`created_at`),
  KEY `for_tags` (`trashed`,`published`,`clan_private`,`created_at`)
) ENGINE=InnoDB AUTO_INCREMENT=44390 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

CREATE TABLE `posts_tags` (
  `id` int(11) NOT NULL auto_increment,
  `post_id` int(11) デフォルト NULL、
  `tag_id` int(11) デフォルト NULL、
  `created_at`日時デフォルトNULL、
  `updated_at` 日時デフォルト NULL、
  主キー (`id`)、
  KEY `index_posts_tags_on_post_id_and_tag_id` (`post_id`,`tag_id`)
) ENGINE=InnoDB AUTO_INCREMENT=63175 DEFAULT CHARSET=utf8
+----+-------------+------------+--------+-------- --------------+------------------------------+---- --+---------------------+-------+-------------- ----------------------------------------------+
| | ID | select_type | テーブル | タイプ | 可能な_キー | キー | key_len | 参照 | 行 | 行 エクストラ |
+----+-------------+------------+--------+-------- --------------+------------------------------+---- --+---------------------+-------+-------------- ----------------------------------------------+
| | 1 | シンプル | 投稿タグ | インデックス | index_post_id_and_tag_id | index_post_id_and_tag_id | 10 | ヌル | 24159 | where を使用します。インデックスの使用; 一時的な使用; ファイルソートの使用 |
| | 1 | シンプル | 投稿 | eq_ref | プライマリ | プライマリ | 4 | .posts_tags.post_id | 1 | | |
+----+-------------+------------+--------+-------- --------------+------------------------------+---- --+---------------------+-------+-------------- ----------------------------------------------+
2 行セット (0.00 秒)

filesort を使用して mysql を回避するには、どのような種類のインデックスを定義する必要がありますか? 注文フィールドがwhere句にない場合は可能ですか?

更新: プロファイリング結果:

mysql> クエリ 1 のプロファイルを表示します。
+--------------------------------+----------------------+
| | ステータス | 期間 |
+--------------------------------+----------------------+
| | 開始 | 0.000027 |
| | クエリのクエリ キャッシュをチェックする | 0.037953 |
| | テーブルを開く | 0.000028 |
| | システムロック | 0.010382 |
| | テーブル ロック | 0.023894 |
| | 初期化 | 0.000057 |
| | 最適化 | 0.010030 |
| | 統計 | 0.000026 |
| | 準備中 | 0.000018 |
| | tmp テーブルの作成 | 0.128619 |
| | 実行中 | 0.000008 |
| | tmp テーブルへのコピー | 1.819463 |
| | ソート結果 | 0.001092 |
| | データの送信 | 0.004239 |
| | 終了 | 0.000012 |
| | tmp テーブルの削除 | 0.000885 |
| | 終了 | 0.000006 |
| | 終了 | 0.000005 |
| | クエリ終了 | 0.000006 |
| | 結果をクエリキャッシュに保存 | 0.000005 |
| | アイテム解放 | 0.000021 |
| | テーブルを閉じる | 0.000013 |
| | 遅いクエリのロギング | 0.000004 |
| | クリーンアップ | 0.000006 |
+--------------------------------+----------------------+

update2:

実際のクエリ (いくつかのブール フィールド、役に立たないインデックス)

SELECT `posts`.* FROM `posts` INNER JOIN `posts_tags`
   ON `posts`.id = `posts_tags`.post_id
   WHERE ((`posts_tags`.tag_id = 7971))
       AND (((posts.trashed = 0)
       AND (`投稿`.`公開` = 1
       AND `posts`.`clan_private` = 0))
       AND ((`posts_tags`.tag_id = 7971)))  
   ORDER BY created_at DESC LIMIT 0, 10;

空のセット (1.25 秒)

ORDER BY なし — 0.01 秒。

+----+-------------+------------+--------+-------- ----------------------------------+---------------- -------+---------+---------------------+-------+-- ------------------------+
| | ID | select_type | テーブル | タイプ | 可能な_キー | キー | key_len | 参照 | 行 | 行 エクストラ |
+----+-------------+------------+--------+-------- ----------------------------------+---------------- -------+---------+---------------------+-------+-- ------------------------+
| | 1 | シンプル | 投稿タグ | インデックス | index_posts_tags_on_post_id_and_tag_id | index_posts_tags_... | 10 | ヌル | 23988 | where を使用します。インデックスの使用 |
| | 1 | シンプル | 投稿 | eq_ref | PRIMARY,index_posts_on_trashed_and_crea | プライマリ | 4 | .posts_tags.post_id | 1 | where | の使用
+----+-------------+------------+--------+-------- ----------------------------------+---------------- -------+---------+---------------------+-------+-- ------------------------+

解決

  1. 「ORDER BY posts_tags.created_at DESC」に更新されたクエリ (アプリ コードの 2 つの小さな変更)
  2. インデックスが追加されました: index_posts_tags_on_created_at.

それで全部です!

4

3 に答える 3

3

少し非正規化し、posts.created_at フィールドを post_tags テーブルにコピーする必要があります (私はそれを post_created_at と呼びましたが、好きなように名前を付けることができます):

CREATE TABLE `posts_tags` (
  `id` int(11) NOT NULL auto_increment,
  `post_id` int(11) default NULL,
  `tag_id` int(11) default NULL,
  `post_created_at` datetime default NULL,
  `created_at` datetime default NULL,
  `updated_at` datetime default NULL,
  PRIMARY KEY  (`id`),
  KEY `index_posts_tags_on_post_id_and_tag_id` (`post_id`,`tag_id`)
) ENGINE=InnoDB;

次に、posts_tags にインデックスを追加します。

(tag_id, post_created_at)

これにより、クエリはファイルソートなしで正しい順序でタグのすべての投稿を取得できます。

于 2010-06-10T19:45:27.827 に答える
1

index_posts_tags_on_post_id_and_tag_idKEY ( post_id, tag_id) を KEY index_posts_tags_tag_id( tag_id) に変更して、Explain を再投稿してみてください。

Posts_Tags 内の TagID の分布は何ですか?

于 2010-06-10T14:42:07.057 に答える
0

キーindex_posts_on_created_atは昇順でソートされていますが、結果を降順でソートしたい

于 2010-06-10T14:38:01.250 に答える