1

次の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秒続くクエリを生成してみました)。bnbbnb_idpicsis_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秒をはるかに超えています。

ボトルネックがどこにあるのか、そしてそれを回避する方法を誰かが知っていますか?

4

1 に答える 1

1

このような結合を使用してクエリを再構築してみることができます(正しくない場合は申し訳ありませんが、1つのテーブルしか記述していません)。

SELECT
  bnb.*, bnbdata_a.*, 
  pos.lat, pos.lng
  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 bnb
JOIN bnbdata 
  ON bnb.id = bnbdata.bnb_id
JOIN positions pos 
  ON bnb.id = pos.bnb_id
LEFT JOIN pics 
  ON bnb.id = pics.bnb_id AND pics.is_cover = 1
LEFT JOIN prices 
  ON bnb.id = prices.bnb_id 
LEFT JOIN (SELECT COUNT(*) AS count, bnb_id FROM reviews GROUP BY bnb_id) reviews
  ON bnb.id = reviews.bnb_id
LEFT JOIN (SELECT COUNT(*) AS count, bnb_id FROM likes GROUP BY bnb_id) likes
  ON bnb.id = likes.bnb_id
WHERE
  lng BETWEEN _LNGA AND _LNGB AND lat BETWEEN _LATA AND _LATB AND distance < 10
ORDER BY distance
LIMIT 0, 25

またはそのように再構築します:

SELECT tmp_bnb.*,
  pics.img_path_thumb_grid,
  reviews.count reviewsCount,
  likes.count likesCount 
FROM     
  (
    SELECT
      bnb.*, bnbdata_a.*, 
      pos.lat, pos.lng
      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
    FROM bnb
    JOIN bnbdata 
      ON bnb.id = bnbdata.bnb_id
    JOIN positions pos 
      ON bnb.id = pos.bnb_id
    WHERE
      lng BETWEEN _LNGA AND _LNGB AND lat BETWEEN _LATA AND _LATB AND distance < 10
    ORDER BY distance
    LIMIT 0, 25
  ) as tmp_bnb
LEFT JOIN pics 
  ON tmp_bnb.id = pics.bnb_id AND pics.is_cover = 1
LEFT JOIN prices 
  ON tmp_bnb.id = prices.bnb_id 
LEFT JOIN (SELECT COUNT(*) AS count, bnb_id FROM reviews GROUP BY bnb_id) reviews
  ON tmp_bnb.id = reviews.bnb_id
LEFT JOIN (SELECT COUNT(*) AS count, bnb_id FROM likes GROUP BY bnb_id) likes
  ON tmp_bnb.id = likes.bnb_id

または、クエリを2つに分割し、最初のクエリで基本情報を取得し、2番目にrewiewsカウントやlikesカウントなどの追加情報を取得することもできます。

reviews_counterまた、テーブルにとlikes_counterを追加し、bnb毎回カウントするのではなく、ある時間(時間マビー)に1回カウントするか、挿入トリガーを使用してインクリメントすることをお勧めします。また、sonsiderは、表cover_pic_idの表紙の画像のIDを保持する新しい列を追加しますbnb

パフォーマンスについて教えてください。

于 2012-11-30T12:35:15.677 に答える