1

私はMySQL 5.5.29-0ubuntu0.12.04.1に取り組んでいます。

結果を日付およびスコアでソートできるクエリを作成する必要があります。

クエリを最適化する方法について、ドキュメントとスタックオーバーフローに関する投稿 (特にthis ) を読みましたが、まだうまくいくのに苦労しています。重要な発見は、一時テーブルの使用を避けるために、ORDER BY または GROUP BY には結合キューの最初のテーブルの列のみが含まれている必要があるということです。そのため、STRAIGHT_JOIN 句と 2 つのわずかに異なるクエリを使用しています。

混乱を避けるために、さまざまなクエリ構成に番号を割り当てます。

  1. STRAIGHT_JOIN 句を使用した日付順
  2. STRAIGHT_JOIN 句を使用したスコアによる並べ替え
  3. STRAIGHT_JOIN 句を使用しない日付順
  4. STRAIGHT_JOIN 句を使用しないスコア順

以下はクエリ 1 で、完了までに約 2.5 秒かかります。

SELECT STRAIGHT_JOIN item.id AS id
FROM item 
INNER JOIN score ON item.id = score.item_id 
LEFT JOIN url ON item.url_id = url.id 
LEFT JOIN doc ON url.doc_id = doc.id 
INNER JOIN feed ON feed.id = item.feed_id 
INNER JOIN user_feed ON feed.id = user_feed.feed_id AND score.user_id = user_feed.user_id 
LEFT JOIN star ON item.id = star.item_id AND score.user_id = star.user_id 
JOIN unseen ON item.id = unseen.item_id AND score.user_id = unseen.user_id 
WHERE score.user_id = 1 AND user_feed.id = 7 
ORDER BY zen_time DESC 
LIMIT 0, 10

以下はクエリ 2 です (最初の結合テーブルが逆になり、順序付け列が異なります)、完了するまでに約 0.01 秒しかかかりません。

SELECT STRAIGHT_JOIN item.id AS id
FROM score
INNER JOIN item ON item.id = score.item_id 
LEFT JOIN url ON item.url_id = url.id 
LEFT JOIN doc ON url.doc_id = doc.id 
INNER JOIN feed ON feed.id = item.feed_id 
INNER JOIN user_feed ON feed.id = user_feed.feed_id AND score.user_id = user_feed.user_id 
LEFT JOIN star ON item.id = star.item_id AND score.user_id = star.user_id 
JOIN unseen ON item.id = unseen.item_id AND score.user_id = unseen.user_id 
WHERE score.user_id = 1 AND user_feed.id = 7 
ORDER BY score DESC 
LIMIT 0, 10

以下は、クエリの EXPLAIN 結果です。

クエリ 1 の説明: ここに画像の説明を入力

クエリ 2 の説明: ここに画像の説明を入力

クエリ 3 の説明: ここに画像の説明を入力

クエリ 4 の説明: ここに画像の説明を入力

クエリ 1 のプロファイラーの結果: ここに画像の説明を入力

クエリ 2 のプロファイラーの結果: ここに画像の説明を入力

クエリ 3 のプロファイラーの結果: ここに画像の説明を入力

クエリ 4 のプロファイラーの結果: ここに画像の説明を入力

以下は、テーブルの定義です。

CREATE TABLE `doc` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`md5` char(32) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `Md5_index` (`md5`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `feed` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`url` text NOT NULL,
`title` text,
PRIMARY KEY (`id`),
FULLTEXT KEY `Title_url_index` (`title`,`url`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `item` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`feed_id` bigint(20) unsigned NOT NULL,
`url_id` bigint(20) unsigned DEFAULT NULL,
`md5` char(32) NOT NULL,
PRIMARY KEY (`id`),
KEY `Md5_index` (`md5`),
KEY `Zen_time_index` (`zen_time`),
KEY `Feed_index` (`feed_id`),
KEY `Url_index` (`url_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `score` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned NOT NULL,
`item_id` bigint(20) unsigned NOT NULL,
`score` float DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `User_item_index` (`user_id`,`item_id`),
KEY Score_index (`score`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `star` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned NOT NULL,
`item_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `User_item_index` (`user_id`,`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `unseen` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned NOT NULL,
`item_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `User_item_index` (`user_id`,`item_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `url` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`doc_id` bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY Doc_index (`doc_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `IDX_Email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `user_feed` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned NOT NULL,
`feed_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `User_feed_index` (`user_id`,`feed_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

クエリに含まれるテーブルの行数は次のとおりです。

Score: 68657
Item: 197602
Url: 198354
Doc: 186113
Feed: 754
User_feed: 721
Star: 0
Unseen: 150762

私のプログラムは zen_time とスコアの両方で可能な限り最速の方法で結果を並べ替えることができる必要があるため、どのアプローチを取るべきですか?

4

1 に答える 1

0

クエリの速度が異なるため、達成したいさまざまな結果に基づいて、さらに正確な分析を行うことにしました。

必要な結果セットは次の 4 つです。

  1. 特定のフィードからすべての項目を選択し、SCORE.score で並べ替えます (インテリジェントな順序)
  2. 特定のフィードからすべてのアイテムを選択し、ITEM.zen_time (時間順) で並べ替えます。
  3. すべてのアイテムを選択し、SCORE.score (インテリジェントな順序) で並べ替えます。
  4. すべてのアイテムを選択し、ITEM.zen_time (時間順) で並べ替えます。

クエリはこれらの条件に適合させる必要があり、その可変部分は次のとおりです。

  • STRAIGHT_JOIN はい/いいえ
  • 最初の JOIN テーブル スコア/アイテム
  • 特定のフィードの WHERE 条件 yes/no
  • ORDER BY スコア/zen_time

すべてのテストは、SELECT SQL_NO_CACHE 命令で実行されています。

結果は次のとおりです。 ここに画像の説明を入力

今、私がしなければならないことは明らかです:

  1. STRAIGHT_JOIN なし、最初の JOIN テーブル SCORE
  2. STRAIGHT_JOIN なし、最初の JOIN テーブル SCORE
  3. STRAIGHT_JOIN (私はここで MySQL エンジンを打ち負かしました :D )、最初の JOIN テーブル SCORE
  4. STRAIGHT_JOIN (私はここで MySQL エンジンを打ち負かしました :D )、最初の JOIN テーブル ITEM
于 2013-10-07T13:19:48.293 に答える