IPによる国の決定:アルゴリズムの速度をテストします

IPで国を決定するには、IPアドレスの範囲とそれぞれの国で構成される特別なデータベースが必要です。 通常、このようなデータベースは、DBMSで使用するためのCSVファイルまたはSQLファイル、または特別な形式のバイナリファイルの形式で配布されます。



MaxMindのGeoIP Countryの無料版である2月のベースGeoLite Countryがテスト用に選択されました。



このトピックに関するいくつかの一般的なソリューションと私の「バイク」がテストに参加しました。



テスト参加者



MySQL


MySQLは実験的なDBMSとして使用されます。 IP範囲と国番号で構成されるテーブルが作成され、IPは整数に変換され、それらに基づいてインデックスが構築されます。 テーブル構造は次のようになります。



CREATE TABLE `ip2country ` ( `ipn1` INT(10) UNSIGNED NOT NULL, `ipn2` INT(10) UNSIGNED NOT NULL, `num` TINYINT(3) UNSIGNED NOT NULL, PRIMARY KEY (`ipn1`), INDEX `ipn2` (`ipn2`) ) ENGINE=MyISAM;
      
      





MySQLの場合、3つのクエリがテストされます。

  1. シンプル

     SELECT `num` FROM `ip2country` WHERE `ipn1` <= INET_ATON(' IP ') AND `ipn2` >= INET_ATON('IP')
          
          



  2. 間。

     SELECT num FROM `ip2country` WHERE INET_ATON('IP') BETWEEN `ipn1` AND `ipn2`
          
          



  3. サブセレクト。

     SELECT num FROM (SELECT * FROM ip2country WHERE `ipn1` <= INET_ATON('IP') ORDER BY `ipn1` DESC LIMIT 1) AS t WHERE `ipn2` >= INET_ATON('IP')
          
          



GeoIP API
GeoIPは、PHPのネイティブAPIとバイナリ形式のデータベースを使用します。 次の2つのモードでテストされます。

SxGeo v2
私の「自転車」について一言。 約6年前、当時利用可能なIPで国を決定するためのソリューションを研究した後、バイナリGeoIP形式の速度に感銘を受けました。 しかし、彼には、適切なIPを見つけるための多数のファイルナビゲーションが欠けていたようです。 その実装について興味深いアイデアが生まれました。 これは非常に迅速に実装され、驚くべきことに、予想よりはるかに高速であることが判明しました。 長い間、Sypex Geoは彼らのプロジェクトで使用されていました。



先日、私はさらに最適化のアイデアを実装することにしました。 その結果、Sypex Geo 2(略称SxGeo)のバージョンが登場しました。 データベースファイルは最初のバージョンより25%小さくなり、同時に速度は1.7〜2倍になりました。



GeoIPおよびその他のソリューションに対する主な利点。

SxGeoは3つのモードでテストされます。

ジオバザ
また、Geobazaアルゴリズムは競合他社とのテストが行​​われました。 競合がありません。これは、ネイティブバイナリファイルが使用されたため、範囲が大幅に増えたためです。 Geobazaは約2000-3000 IP /秒を示しましたが、結果には非常に大きなばらつきがありました。 Geobazaの作成者がこの記事を読み、2月のGeoLite Countryによって生成されたファイルを送信する場合、喜んでテストに追加します。



テスト中



テストのために、PHPスクリプトが作成され、各開始時に10,000個のランダムIPアドレスの配列が生成されました。 その後、すべてのアルゴリズムがこのアレイでテストされました。 このテスト方法は、アルゴリズムが同じ条件になるように選択されました。



FreeBSD 8およびPHP 5.2.17を実行しているサーバーでテスト済み。 また、Win 7 x64、PHP 5.3.9でもテストされ、比率はほぼ同じであるため、表にはFreeBSDの結果のみが示されています。

テストは10回実行され、平均データがグラフに表示されます。





最も遅いのは、単純なMySQLクエリであることが判明しました。 このような遅い操作の理由は、これらのクエリのEXPLAINを見ると明らかになります。



 EXPLAIN SELECT num FROM `ip2country` WHERE ipn1 <= INET_ATON('88.88.88.88') AND ipn2 >= INET_ATON('88.88.88.88') LIMIT 1;
      
      





 id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE ip2country range PRIMARY PRIMARY 4 NULL 51059 Using where 1 SIMPLE ip2country range PRIMARY PRIMARY 4 NULL 53852 Using where 1 SIMPLE ip2country range PRIMARY,ipn2 PRIMARY 4 NULL 51587 Using where
      
      







最初の結果は単純なPRIMARY KEYインデックス( `ipn1`)で、2番目は複合PRIMARY KEYインデックス(` ipn1`、 `ipn2`)で、3番目は2つのPRIMARY KEYインデックス(` ipn1`)、INDEX `ipn2`(` ipn2`)です。 他の場合よりも後の列挙のための行の複合インデックスを見ることができます。 LIMIT 1なしでテストしました。この場合、EXPLAINはインデックスが使用されていないことを書き込みますが、このオプションは実際にはLIMITを使用した場合よりも速く動作します。



ネストされたSELECTを使用したオプションははるかに高速です。 このクエリは、MySQLのインデックスが通常使用されると、非常に高速に動作し、特殊なバイナリ形式に近づくことを示しています。



GeoIPは、MySQLよりも使用することが望ましいことを示しています。 メモリ内のキャッシュを使用すると、10%未満というわずかな増加しか得られないと混乱しました。 geoip.incを掘り下げて、犯人を見つけました。 次のコードであることが判明しました。



 if ($gi->flags & GEOIP_MEMORY_CACHE) { // workaround php's broken substr, strpos, etc handling with // mbstring.func_overload and mbstring.internal_encoding $enc = mb_internal_encoding(); mb_internal_encoding('ISO-8859-1'); $buf = substr($gi->memory_buffer, 2 * $gi->record_length * $offset, 2 * $gi->record_length); mb_internal_encoding($enc); }
      
      





mb_internal_encodingを使用して行をコメントアウトすると、最終的に速度は6600 IP /秒に上昇します。これは、メモリでのキャッシュの使用による顕著な増加です。 この場合のエンコーディングは気になりません。GeoIPCityに何らかの不具合があったのかもしれません。



SxGeoに関しては、コメントは不要だと思います。 また、通常モードでも非常に高速に動作し、バッチ+メモリモードでは、さらに40%増加できます。



ご希望の方は、SxGeo 2をダウンロードしてテストできます 。 要望やバグ報告は大歓迎です。



UPD。 また、インデックスのさまざまな組み合わせでクエリをテストしました。最初の2つのクエリにLIMIT 1を追加すると、MySQLが非常に鈍くなり、3〜5倍遅くなります。



All Articles