私は同様の問題に取り組んでいました。そこでは、約400万のIP範囲を持つデータベースを検索し、スキャンされた行の数を400万から約5に減らす優れたソリューションを見つけました(IPによって異なります):
このSQLステートメント:
SELECT id FROM geoip WHERE $iplong BETWEEN range_begin AND range_end
に変換されます:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_end >= $iplong
問題は、MySQLが'range_begin <=$ iplong'のすべての行を取得し、'range_end> =$iplong'の場合にスキャンする必要があることです。この最初のAND条件(range_begin <=$ iplong)は約200万行を取得し、range_endが一致するかどうかをすべてチェックする必要があります。
ただし、これは1つのAND条件を追加することで大幅に簡略化できます:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_begin >= $iplong-65535 AND range_end >= $iplong
ステートメント
range_begin <= $iplong AND range_begin >= $iplong-65535
range_beginが$iplong-65535と$iplongの間にあるエントリのみを取得します。私の場合、これにより、取得される行の数が4Mioから減少しました。約5になり、スクリプトの実行時間は数分から数秒に短縮されました。
65535に関する注意 :これは私のテーブルの場合、range_beginとrange_endの間の最大距離です。つまり、すべての行で(range_end-range_begin)<=65535です。 IP範囲が大きい場合は、65535を増やす必要があります。IP範囲が小さい場合は、この定数を減らすことができます。この定数が大きすぎる場合(たとえば40億)、クエリ時間を節約できません。
このクエリでは、range_beginのインデックスのみが必要です。