次のMySQLテーブルがあります。
CREATE TABLE IF NOT EXISTS `pics` (
`id` mediumint(8) unsigned NOT NULL auto_increment,
`bnb_id` mediumint(7) unsigned NOT NULL,
`img_path` varchar(128) NOT NULL,
`img_path_gallery` varchar(128) NOT NULL,
`img_path_thumb_small` varchar(128) NOT NULL,
`img_path_thumb_large` varchar(128) NOT NULL,
`img_path_thumb_grid` varchar(128) NOT NULL,
`title` varchar(80) NOT NULL,
`order` tinyint(2) NOT NULL,
`upload_date` datetime NOT NULL,
`state` enum('LOCAL','S3') NOT NULL default 'LOCAL',
`is_cover` tinyint(1) unsigned default NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `bnb_id_2` (`bnb_id`,`is_cover`),
KEY `bnb_id` (`bnb_id`),
KEY `is_cover` (`is_cover`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=30371 ;
is_cover
は、それぞれに1つの画像のみを選択するために作成したフィールドですbnb_id
。画像が表紙として選択されている場合は1に設定され、それ以外の場合は1に設定されNULL
ます。LEFT JOIN
別のテーブルに移動する必要があります。それを呼び出しましょうbnb
。各エントリのpics
テーブルに複数の行がある場合があります(参照整合性がバインドされています)が、この場合、テーブルから1つの行のみを抽出する必要があるため、列とすべてのインデックス付けが必要です(他のすべてのソリューションI 10〜50秒続くクエリを生成してみました)。bnb
bnb_id
pics
is_cover
bnb
ただし、この場合でも、クエリは非常に遅く、テーブル内の約10000要素とテーブル内の30000要素のデータプールでそれぞれ5〜8秒の実行が必要pics
です。= 1のテーブルから選択するのis_cover
は非常に高速で簡単ですが、より大きなクエリを入力すると、すべてが分割されます。
SELECT subbnb.*,
3956 * 2 * ASIN(
SQRT(
POWER(
SIN((_LAT - abs(lat)) * pi()/180 / 2),
2) +
COS(_LAT * pi()/180 ) *
COS(abs(lat) * pi()/180) *
POWER(
SIN((_LNG - abs(lng)) * pi()/180 / 2),
2)
)
) AS distance,
prices.price,
pics.img_path_thumb_grid,
reviews.count reviewsCount,
likes.count likesCount
FROM
(SELECT
bnb.*,
bnbdata_a.*,
pos.lat,
pos.lng
FROM bnb
JOIN bnbdata
ON (bnb.id = bnbdata.bnb_id)
JOIN positions pos
ON (bnb.id = pos.bnb_id)
) subbnb
LEFT JOIN (
SELECT *
FROM pics
WHERE is_cover = 1
) pics
ON (subbnb.id = pics.bnb_id)
LEFT JOIN (SELECT price, bnb_id FROM prices WHERE category = "DAILY") prices
ON (subbnb.id = prices.bnb_id)
LEFT JOIN (SELECT COUNT(*) AS count, bnb_id FROM reviews GROUP BY bnb_id) reviews
ON (subbnb.id = reviews.bnb_id)
LEFT JOIN (SELECT COUNT(*) AS count, bnb_id FROM likes GROUP BY bnb_id) likes
ON (subbnb.id = likes.bnb_id)
WHERE
lng BETWEEN _LNGA AND _LNGB
AND lat BETWEEN _LATA AND _LATB
HAVING distance < 10
ORDER BY distance
LIMIT 0, 25
(_が付加された文字列は実際の数値です)
EXPLAIN
クエリを実行すると、次の結果が生成されます。
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived5> system NULL NULL NULL NULL 0 const row not found
1 PRIMARY <derived6> system NULL NULL NULL NULL 0 const row not found
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 10522 Using where; Using temporary; Using filesort
1 PRIMARY <derived3> ALL NULL NULL NULL NULL 7040
1 PRIMARY <derived4> ALL NULL NULL NULL NULL 1
6 DERIVED likes index NULL PRIMARY 6 NULL 1 Using index
5 DERIVED reviews index NULL bnb_id 5 NULL 1 Using index
4 DERIVED prices ALL NULL NULL NULL NULL 1 Using where
3 DERIVED pics ref is_cover is_cover 2 11760 Using where
2 DERIVED pos ALL PRIMARY NULL NULL NULL 10543
2 DERIVED bnbdata eq_ref PRIMARY PRIMARY 3 db.pos.bnb_id 1
2 DERIVED bnb eq_ref PRIMARY PRIMARY 3 db.pos.bnb_id 1
is_cover
インデックスがMySQL(、id 4)によって無視されているように見えますが、テーブルUsing where
に対して小さな選択を実行すると同じことが起こり、すべてが非常に迅速に行われます。pics
このクエリのボトルネックを見つけることができませんpics
。JOINを削除してすべてを高速化しますが、JOINされたサブクエリ自体は非常に高速であり、残りの大きなクエリも同様です。最初に数学計算コードを使用しても、実行されることはありません。実行の2秒をはるかに超えています。
ボトルネックがどこにあるのか、そしてそれを回避する方法を誰かが知っていますか?