2

私はこのクエリを持っています:

SELECT `country`
FROM `geoip_base`
WHERE 1840344811 BETWEEN `start` AND `stop`

インデックスの使用が不適切であり(使用しますが、テーブルの大部分を解析します)、動作が遅すぎます。ORDER BY と LIMIT を使用してみましたが、役に立ちませんでした。

「開始 <= 1840344811 AND 1840344811 <= 停止」も同様に機能します。

CREATE TABLE IF NOT EXISTS `geoip_base` (
  `start` decimal(10,0) NOT NULL,
  `stop` decimal(10,0) NOT NULL,
  `inetnum` char(33) collate utf8_bin NOT NULL,
  `country` char(2) collate utf8_bin NOT NULL,
  `city_id` int(11) NOT NULL,
  PRIMARY KEY  (`start`,`stop`),
  UNIQUE KEY `start` (`start`),
  UNIQUE KEY `stop` (`stop`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

テーブルには 57,424 行あります。

クエリ "... BETWEEN START AND STOP ORDER BY START LIMIT 1" について説明します: キーstopを使用して 24099 行を取得します。順序と制限がない場合、mysql はキーを使用せず、すべての行を取得します。

4

5 に答える 5

5

テーブルが の場合、インデックスMyISAMを使用してこのクエリを改善できます。SPATIAL

ALTER TABLE
        geoip_base
ADD     ip_range LineString;

UPDATE  geoip_base
SET     ip_range =
        LineString
                (
                Point(-1, `start`),
                Point(1, `stop`)
                );

ALTER TABLE
        geoip_base
MODIFY  ip_range NOT NULL;

CREATE SPATIAL INDEX
        sx_geoip_range ON geoip_base (ip_range);

SELECT  country
FROM    geoip_base
WHERE   MBRContains(ip_range, Point(0, 1840344811)

この記事はあなたに興味があるかもしれません:

または、範囲が交差しない場合 (データベースの性質上、交差しないことを除いて)、UNIQUEインデックスを作成して次のgeoip_base.startクエリを使用できます。

SELECT  *
FROM    geoip_base
WHERE   1840344811 BETWEEN `start` AND `stop`
ORDER BY
        `start` DESC
LIMIT 1;

ORDER BYおよび条件に注意してくださいLIMIT。これらは重要です。

このクエリは次のようになります。

SELECT  *
FROM    geoip_base
WHERE   `start` <= 1840344811
        AND `stop` >= 1840344811
ORDER BY
        `start` DESC
LIMIT 1;

を使用すると、最初の一致 (つまり、入力した値に最も近い範囲) で停止ORDER BY / LIMITする降順のインデックス スキャンを選択するクエリが作成されます。停止時の追加のフィルターは、範囲に this が含まれているかどうかを確認するだけです。startstartIPIP

範囲が交差しないため、この範囲または範囲がまったく含まれていない場合は、目的のものが含まれIPます。

于 2011-04-21T13:36:59.310 に答える
0

テーブルのデザインがオフです。

小数を使用していますが、ゼロを許可していません。そのような数値を格納するためにすぐに 5 バイトを使用し、単純な INT で十分です (4 バイト)。

その後、複合主キー (5 + 5 バイト) を作成し、続いて 2 つの一意の制約 (それぞれ 5 バイト) を作成して、効果的にインデックス ファイルをデータ ファイルとほぼ同じサイズにします。そうすれば、インデックスを作成しても非常に効果がありません。

LIMIT を使用しても、少なくともクエリを作成した方法では、MySQL にインデックスの使用を強制することはありません。何が起こるかというと、MySQL は条件を満たすデータセットを取得し、オフセット制限に準拠していない行を破棄します。

また、MySQL の保護されたキーワード (START や STOP など) を使用することはお勧めできませ。保護されたキーワードを使用して列に名前を付けないでください。

役立つのは、主キーをそのまま作成し、列に個別にインデックスを付けないことです。また、より多くのメモリを使用するように MySQL を構成すると、実行速度が向上します。

テスト目的で、あなたと同様のテーブルを作成し、startandの複合キーを定義しstop、次のクエリを使用しました。

SELECT `country` FROM table WHERE 1500 BETWEEN `start` AND `stop` AND start >= 1500

私のテーブルは InnoDB タイプで、10 万行が挿入されています。クエリはこの方法で 87 行を調べ、数ミリ秒で実行されます。バッファー プールのサイズはテスト マシンのメモリの 90% です。それはあなたのクエリ/データベースインスタンスを最適化するための洞察を与えるかもしれません.

于 2011-04-21T14:00:03.043 に答える
0

上記の Michael JV の例は機能しません: SELECT countryFROM table WHERE 1500 BETWEEN startAND stopAND start >= 1500

BETWEEN start AND stop は start <= 1500 AND end >= 1500 と同じです

したがって、同じ句に start <= 1500 AND start >= 1500 があります。したがって、成功する唯一の方法は、start=1500 の場合であり、オプティマイザーは開始インデックスを使用することを認識しています。

于 2013-12-06T19:02:17.277 に答える