4

約300kのレコードを含むmysql(5.0.22)myisamテーブルがあり、半径5マイル以内で緯度/経度の距離検索を実行したいと考えています。

lat / lonフィールドをカバーするインデックスがあり、lat / lonを選択するだけで高速(ミリ秒応答)になります。しかし、テーブルで追加のフィールドを選択すると、5〜8秒にひどく遅くなります。

全文検索を利用するためにmyisamを使用しています。他のインデックスはうまく機能します(たとえば、slug ='xxxxx'のリストから*を選択します)。

クエリ、テーブル、またはインデックスを最適化して処理を高速化するにはどうすればよいですか?

私のスキーマは次のとおりです。

CREATE TABLE  `Listing` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `name` varchar(125) collate utf8_unicode_ci default NULL,
  `phone` varchar(18) collate utf8_unicode_ci default NULL,
  `fax` varchar(18) collate utf8_unicode_ci default NULL,
  `email` varchar(55) collate utf8_unicode_ci default NULL,
  `photourl` varchar(55) collate utf8_unicode_ci default NULL,
  `thumburl` varchar(5) collate utf8_unicode_ci default NULL,
  `website` varchar(85) collate utf8_unicode_ci default NULL,
  `categoryid` int(10) unsigned default NULL,
  `addressid` int(10) unsigned default NULL,
  `deleted` tinyint(1) default NULL,
  `status` int(10) unsigned default '2',
  `parentid` int(10) unsigned default NULL,
  `organizationid` int(10) unsigned default NULL,
  `listinginfoid` int(10) unsigned default NULL,
  `createuserid` int(10) unsigned default NULL,
  `createdate` datetime default NULL,
  `lasteditdate` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `lastedituserid` int(10) unsigned default NULL,
  `slug` varchar(155) collate utf8_unicode_ci default NULL,
  `aclid` int(10) unsigned default NULL,
  `alt_address` varchar(80) collate utf8_unicode_ci default NULL,
  `alt_website` varchar(80) collate utf8_unicode_ci default NULL,
  `lat` decimal(10,7) default NULL,
  `lon` decimal(10,7) default NULL,
  `city` varchar(80) collate utf8_unicode_ci default NULL,
  `state` varchar(10) collate utf8_unicode_ci default NULL,
  PRIMARY KEY  (`id`),
  KEY `idx_fetch` USING BTREE (`slug`,`deleted`),
  KEY `idx_loc` (`state`,`city`),
  KEY `idx_org` (`organizationid`,`status`,`deleted`),
  KEY `idx_geo_latlon` USING BTREE (`status`,`lat`,`lon`),
  FULLTEXT KEY `idx_name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC;

私の質問は:

SELECT Listing.name, Listing.categoryid, Listing.lat, Listing.lon
, 3956 * 2 * ASIN(SQRT( POWER(SIN((Listing.lat - 37.369195) * pi()/180 / 2), 2) + COS(Listing.lat * pi()/180) * COS(37.369195 * pi()/180) * POWER(SIN((Listing.lon --122.036849) * pi()/180 / 2), 2) )) rawgeosearchdistance
FROM Listing
WHERE
    Listing.status = '2'
    AND ( Listing.lon between -122.10913433498 and -121.96456366502 )
    AND ( Listing.lat between 37.296909665016 and 37.441480334984)
HAVING rawgeosearchdistance < 5
ORDER BY rawgeosearchdistance ASC;

ジオサーチなしで計画を説明する:

    + ---- + ------------- + ------------ + ------- + --------- -------- + ----------------- + --------- + ------ + ------ + ------------- +
    | id | select_type | テーブル| タイプ| possible_keys | キー| key_len | ref | 行| エクストラ|
    + ---- + ------------- + ------------ + ------- + --------- -------- + ----------------- + --------- + ------ + ------ + ------------- +
    | 1 | シンプル| リスト| 範囲| idx_geo_latlon | idx_geo_latlon | 19 | NULL | 453 | whereを使用する|
    + ---- + ------------- + ------------ + ------- + --------- -------- + ----------------- + --------- + ------ + ------ + ------------- +

ジオサーチで計画を説明する:

+ ---- + ------------- + ------------ + ------- + --------- -------- + ----------------- + --------- + ------ + ------ + ----------------------------- +
| id | select_type | テーブル| タイプ| possible_keys | キー| key_len | ref | 行| エクストラ|
+ ---- + ------------- + ------------ + ------- + --------- -------- + ----------------- + --------- + ------ + ------ + ----------------------------- +
| 1 | シンプル| リスト| 範囲| idx_geo_latlon | idx_geo_latlon | 19 | NULL | 453 | whereを使用する; filesortの使用|
+ ---- + ------------- + ------------ + ------- + --------- -------- + ----------------- + --------- + ------ + ------ + ----------------------------- +

これがカバーリングインデックス付きの説明プランです。列を正しい順序にすることで、大きな違いが生まれました。

+ ---- + ------------- + -------- + ------- + ------------- -+ --------------- + --------- + ------ + -------- + ----- ------------------------------------- +
| id | select_type | テーブル| タイプ| possible_keys | キー| key_len | ref | 行| エクストラ|
+ ---- + ------------- + -------- + ------- + ------------- -+ --------------- + --------- + ------ + -------- + ----- ------------------------------------- +
| 1 | シンプル| リスト| 範囲| idx_geo_cover | idx_geo_cover | 12 | NULL | 453 | whereを使用する; インデックスを使用します。filesortの使用|
+ ---- + ------------- + -------- + ------- + ------------- -+ --------------- + --------- + ------ + -------- + ----- ------------------------------------- +

ありがとうございました!

4

5 に答える 5

4

PostgreSQL(Postgisと組み合わせて)の使用を本当に検討する必要があると思います。

次の理由により、地理空間データのためにMySQLを(今のところ)あきらめました。

  • MySQLは、MyISAMテーブルの空間データ型/空間インデックスのみをサポートしますが、MyISAMに固有の欠点(トランザクション、参照整合性など)があります。
  • MySQLは、MBRベース(最小境界矩形)でのみOpenGIS仕様の一部を実装します。これは、最も深刻な地理空間クエリ処理にはほとんど役に立ちません( MySQLマニュアルのこのリンクを参照してください)。この機能の一部は、遅かれ早かれ必要になる可能性があります。

適切な(GIST)空間インデックスと適切なクエリを備えたPostgreSQL/Postgisは非常に高速です。

:「小さな」ポリゴンの選択と500万(!)を超える非常に複雑なポリゴンを含むテーブルとの間でオーバーラップするポリゴンを決定し、これらの結果とソートの間のオーバーラップの量を計算します。平均実行時間:30〜100ミリ秒(この特定のマシンにはコース外に大量のRAMがあります。PostgreSQLインストールを調整することを忘れないでください...(ドキュメントを読んでください))。

于 2009-06-04T17:56:27.273 に答える
1

おそらく、緯度/経度のみのクエリで「カバーインデックス」を使用しています。カバーインデックスは、クエリで使用されるインデックスに、選択しているデータが含まれている場合に発生します。MySQLはインデックスにアクセスするだけでよく、データ行にアクセスする必要はありません。 詳細については、こちらをご覧ください。それはなぜlat/lonクエリがとても速いのかを説明するでしょう。

計算と返される行数が非常に多いため、長いクエリの速度が低下しているのではないかと思います。(さらに、having句用に作成する必要のある一時テーブル)。

于 2009-06-04T17:27:39.423 に答える
0

selectステートメントでそれほど多くの計算を行うことは本当に避けてください。それはおそらくあなたの多くの減速の原因です。SQLはクエリ言語であることを忘れないでください。三角関数用に最適化されていません。

非常に単純な距離検索(より多くの結果を返す)を実行してから結果を選別すると、SQLが高速になり、全体的な結果が高速になります。

クエリで距離を使用する場合は、少なくとも、距離の2乗計算を使用してください。平方根の計算は非常に遅いことで有名です。二乗距離ははるかに使いやすいです。距離の2乗の計算では、距離の代わりに距離の2乗を使用します。それははるかに簡単です。デカルト座標系の場合、直角三角形の短辺の2乗の合計は斜辺の2乗に等しいため、距離を計算するよりも2乗距離を計算する方が簡単です(2つの正方形を合計するだけです)。あなたがしなければならないのは、あなたが比較したい距離を二乗していることを確認することです(正確な距離を見つけてそれをあなたの望む距離(例えば5)と比較する代わりに、あなたは二乗距離を見つけてそれを比較します希望の距離の2乗に(25、

于 2009-06-04T17:23:44.607 に答える
0

ジオ半径検索を実装したとき、すべての米国の郵便番号を緯度経度とともにメモリにロードし、開始点を半径で使用して半径内の郵便番号のリストを取得し、それをデータベースクエリに使用しました。もちろん、検索スペースが 2,000 万行の範囲にあったため、検索に solr を使用していましたが、同じ原則が適用されるはずです。携帯からなので、浅はかな回答で申し訳ありません。

于 2009-06-04T23:13:24.200 に答える
0

リストの数に応じて、次を含むビューを作成できますか

Listing1Id、Listing2ID、距離

基本的には、すべての距離を「事前に計算」するだけです

次に、次のようなことができます。

距離が 5 未満で、listing1ID = XXX である v_Distance d から、listing2ID を選択します。

于 2009-06-04T17:43:22.440 に答える