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つのクエリがテストされます。
- シンプル
SELECT `num` FROM `ip2country` WHERE `ipn1` <= INET_ATON(' IP ') AND `ipn2` >= INET_ATON('IP')
- 間。
SELECT num FROM `ip2country` WHERE INET_ATON('IP') BETWEEN `ipn1` AND `ipn2`
- サブセレクト。
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つのモードでテストされます。- Standartはデフォルトモードです。
- メモリ -データベースをメモリにキャッシュします。
SxGeo v2
私の「自転車」について一言。 約6年前、当時利用可能なIPで国を決定するためのソリューションを研究した後、バイナリGeoIP形式の速度に感銘を受けました。 しかし、彼には、適切なIPを見つけるための多数のファイルナビゲーションが欠けていたようです。 その実装について興味深いアイデアが生まれました。 これは非常に迅速に実装され、驚くべきことに、予想よりはるかに高速であることが判明しました。 長い間、Sypex Geoは彼らのプロジェクトで使用されていました。先日、私はさらに最適化のアイデアを実装することにしました。 その結果、Sypex Geo 2(略称SxGeo)のバージョンが登場しました。 データベースファイルは最初のバージョンより25%小さくなり、同時に速度は1.7〜2倍になりました。
GeoIPおよびその他のソリューションに対する主な利点。
- 小さい基本サイズは、範囲ごとに4バイトを少し超えています。 たとえば、GeoIPバイナリベースの重量は1.2 MBですが、SxGeo 2の重量は0.62 MBです。
- 非常に高速な処理速度(テスト結果を参照)。
- ディスクからの読み取りの最小数(3 + 1 * N、NはIPの数)。
- バッチ処理の追加モード。
- ファイルは、単一IP処理に推奨される通常モードです。
- バッチ -一度に多くのIPアドレスを処理するように設計されたバッチ処理モード。
- バッチ+メモリ -このモードでは、メモリ内のデータベースキャッシュが追加で使用されます。 最速モードですが、より多くのメモリが必要です。 データベースファイル全体がメモリにロードされます。
ジオバザ
また、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倍遅くなります。