エントリー
長い間、私はzapimirのSxGeoライブラリを使用していました 。 そして最近まで、すべてが私に合っていました。 データベースにデータを追加する必要がなくなるまで満足。
インターネットでSxGeoからデータパッカーを見つけられず、開発者に必要な機能を要求する強さを見つけられなかったため、松葉杖を書くことにしました。 この決定は、使用するライブラリのさらに2つの欠点の影響を受けましたが、
- ディレクトリ数の制限。
- 目的のアドレスを含むアドレス範囲を知ることができない。
- packagistにパッケージがありません。
実際、私はあなたと私の開発を共有しています。
プロトタイプと私のソリューションの違い:
- IPToolはデータベースを作成して検索するための単なるツールであり、SxGeoプロジェクトはツールだけでなくデータベース自体も提供するプロジェクトです。
- IPToolデータベースはより多くのスペースを使用します(範囲の最初のアドレスは完全に保存され、4バイトを使用しますが、SxGeoでは3バイトのみです)。
- IPToolには、ディスクからデータを読み取るモードが1つしかありません(データベースをメモリにロードするモードは計画に含まれています)。
- データに加えて、IPToolはアドレスを含むIPアドレスの範囲を返します。
- IPToolは、ディレクトリから(すべてまたはシリアル番号で)データを取得するためのメソッドを提供します。
- IPToolデータベースは、データベース自体のライセンスを取得する機能を提供します。
- IPToolはComposerを使用して簡単にインストールできます。
使用する
IPツールの初期化
/* - /path/to/iptool.database */ $iptool = new \Ddrv\Iptool\Iptool('/path/to/iptool.database');
データベース情報の取得
print_r($iptool->about());
Array ( [created] => 1507199627 [author] => Anonymous Author [license] => MIT [networks] => Array ( [count] => 276148 [data] => Array ( [country] => Array ( [0] => code [1] => name ) ) ) )
IPアドレス情報の検索
print_r($iptool->find('81.32.17.89'));
Array ( [network] => Array ( [0] => 81.32.0.0 [1] => 81.48.0.0 ) [data] => Array ( [country] => Array ( [code] => es [name] => Spain ) ) )
ディレクトリのすべての要素を取得します
print_r($iptool->getRegister('country'));
Array ( [1] => Array ( [code] => cn [name] => China ) [2] => Array ( [code] => es [name] => Spain ) ... [N] => Array ( [code] => jp [name] => Japan ) )
シリアル番号でディレクトリアイテムを取得する
print_r($iptool->getRegister('country',2));
Array ( [code] => cn [name] => China ) )
データベースを作成するプロセスはより時間がかかりますが、リポジトリおよびGitHub wikiでロシア語と英語の英語版で入手可能なドキュメントで説明されています。
UPD1。 IPToolとSxGeoの速度の比較
結果の信頼性を高めるために、SxGeoデータに基づいてIPToolのデータベースを作成しました
ベンチマークの準備
$ cd /path/to/test/dir $ mkdir csv $ mkdir csv/sxgeo $ mkdir t
SxGeo.phpおよびSxGeoCity.datファイルを現在のディレクトリ(/ path / to / test / dir)にコピーする必要があります
IPToolをインストールする
$ composer require ddrv/iptool:~1.0
SxGeo DBをCSVファイルにインポートする
<?php /* SxGeo csv */ include_once __DIR__.DIRECTORY_SEPARATOR.'SxGeo.php'; class ExtSxGeo extends SxGeo { public function parseBase() { $s=0; $firstIp = '0.0.0.0'; $seek = 0; $data = $this->parseCity($seek,1); $sxNet = fopen(__DIR__.DIRECTORY_SEPARATOR.'csv'.DIRECTORY_SEPARATOR.'sxgeo'.DIRECTORY_SEPARATOR.'sxNet.csv','w'); $sxCnt = fopen(__DIR__.DIRECTORY_SEPARATOR.'csv'.DIRECTORY_SEPARATOR.'sxgeo'.DIRECTORY_SEPARATOR.'sxCnt.csv','w'); $sxRgn = fopen(__DIR__.DIRECTORY_SEPARATOR.'csv'.DIRECTORY_SEPARATOR.'sxgeo'.DIRECTORY_SEPARATOR.'sxRgn.csv','w'); $sxCts = fopen(__DIR__.DIRECTORY_SEPARATOR.'csv'.DIRECTORY_SEPARATOR.'sxgeo'.DIRECTORY_SEPARATOR.'sxCts.csv','w'); $ids = [ 'cnt' => [], 'rgn' => [], 'cts' => [], ]; for ($octet=1;$octet<=223;$octet++) { $bip = pack('C',$octet); $min = $this->b_idx_arr[$octet-1]; $max = $this->b_idx_arr[$octet]; for ($b=$min; $b<=$max;$b++) { fseek($this->fh, $this->db_begin + $b * $this->block_len); $block = fread($this->fh, $this->block_len); $i = unpack('C4',$bip.substr($block,0,3)); $ip = implode('.',$i); $lastIp = long2ip(ip2long($ip)-1); $csvNet = [ $firstIp, $lastIp, $data['city']['id'], $data['region']['id'], $data['country']['id'], ]; fputcsv($sxNet,$csvNet); if (!isset($ids['cts'][$data['city']['id']])) { $ids['cts'][$data['city']['id']] = true; $csvCts = [ $data['city']['id'], $data['city']['lat'], $data['city']['lon'], $data['city']['name_ru'], $data['city']['name_en'], ]; fputcsv($sxCts,$csvCts); } if (!isset($ids['rgn'][$data['region']['id']])) { $ids['rgn'][$data['region']['id']] = true; $csvRgn = [ $data['region']['id'], $data['region']['iso'], $data['region']['name_ru'], $data['region']['name_en'], ]; fputcsv($sxRgn,$csvRgn); } if (!isset($ids['cnt'][$data['country']['id']])) { $ids['cnt'][$data['country']['id']] = true; $csvCnt = [ $data['country']['id'], $data['country']['iso'], $data['country']['lat'], $data['country']['lon'], $data['country']['name_ru'], $data['country']['name_en'], ]; fputcsv($sxCnt,$csvCnt); } $firstIp = $ip; $seek = hexdec(bin2hex(substr($block, $this->block_len - $this->id_len, $this->id_len))); $data = $this->parseCity($seek,1); } } $lastIp = '255.255.255.255'; $csvNet = [ $firstIp, $lastIp, $data['city']['id'], $data['region']['id'], $data['country']['id'], ]; fputcsv($sxNet,$csvNet); if (!isset($ids['cts'][$data['city']['id']])) { $ids['cts'][$data['city']['id']] = true; $csvCts = [ $data['city']['id'], $data['city']['lat'], $data['city']['lon'], $data['city']['name_ru'], $data['city']['name_en'], ]; fputcsv($sxCts,$csvCts); } if (!isset($ids['rgn'][$data['region']['id']])) { $ids['rgn'][$data['region']['id']] = true; $csvRgn = [ $data['region']['id'], $data['region']['iso'], $data['region']['name_ru'], $data['region']['name_en'], ]; fputcsv($sxRgn,$csvRgn); } if (!isset($ids['cnt'][$data['country']['id']])) { $ids['cnt'][$data['country']['id']] = true; $csvCnt = [ $data['country']['id'], $data['country']['iso'], $data['country']['lat'], $data['country']['lon'], $data['country']['name_ru'], $data['country']['name_en'], ]; fputcsv($sxCnt,$csvCnt); } fclose($sxNet); fclose($sxCnt); fclose($sxRgn); fclose($sxCts); } } $sxgeo = new ExtSxGeo( __DIR__.DIRECTORY_SEPARATOR.'SxGeoCity.dat',2); $sxgeo->parseBase();
スクリプトを実行して待機します。
$ php import.php
受信したcsvファイルからIPToolデータベースを作成する
<?php /* IPTool csv */ require_once(__DIR__.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php'); /* . . */ $tmpDir = __DIR__.'/t'; /* Converter. */ $converter = new \Ddrv\Iptool\Converter($tmpDir); /* . . */ $dbFile = __DIR__.DIRECTORY_SEPARATOR.'iptool.sxgeo.city.dat'; /* CSV . */ $sxNet = __DIR__.DIRECTORY_SEPARATOR.'csv'.DIRECTORY_SEPARATOR.'sxgeo'.DIRECTORY_SEPARATOR.'sxNet.csv'; $sxCnt = __DIR__.DIRECTORY_SEPARATOR.'csv'.DIRECTORY_SEPARATOR.'sxgeo'.DIRECTORY_SEPARATOR.'sxCnt.csv'; $sxRgn = __DIR__.DIRECTORY_SEPARATOR.'csv'.DIRECTORY_SEPARATOR.'sxgeo'.DIRECTORY_SEPARATOR.'sxRgn.csv'; $sxCts = __DIR__.DIRECTORY_SEPARATOR.'csv'.DIRECTORY_SEPARATOR.'sxgeo'.DIRECTORY_SEPARATOR.'sxCts.csv'; /* . */ $converter->setAuthor('Ivan Dudarev'); /* . */ $converter->setLicense('MIT'); /* CSV. */ $converter->addCSV('sxNet',$sxNet); $converter->addCSV('sxCnt',$sxCnt); $converter->addCSV('sxRgn',$sxRgn); $converter->addCSV('sxCts',$sxCts); /* Country. */ $country = array( 'id' => array( 'type' => 'int', 'column' => 0, ), 'iso' => array( 'type' => 'string', 'column' => 1, 'transform' => 'low', ), 'lat' => array( 'type' => 'double', 'column' => 2, ), 'lon' => array( 'type' => 'double', 'column' => 3, ), 'nameRu' => array( 'type' => 'string', 'column' => 4, ), 'nameEn' => array( 'type' => 'string', 'column' => 5, ), ); $converter->addRegister('country','sxCnt',0, $country); /* Region. */ $region = array( 'id' => array( 'type' => 'int', 'column' => 0, ), 'iso' => array( 'type' => 'string', 'column' => 1, 'transform' => 'low', ), 'nameRu' => array( 'type' => 'string', 'column' => 2, ), 'nameEn' => array( 'type' => 'string', 'column' => 3, ), ); $converter->addRegister('region','sxRgn',0, $region); /* City. */ $city = array( 'id' => array( 'type' => 'int', 'column' => 0, ), 'lat' => array( 'type' => 'double', 'column' => 1, ), 'lon' => array( 'type' => 'double', 'column' => 2, ), 'nameRu' => array( 'type' => 'string', 'column' => 3, ), 'nameEn' => array( 'type' => 'string', 'column' => 4, ), ); $converter->addRegister('city','sxCts',0, $city); /* . */ $data = array( 'city' => 2, 'region' => 3, 'country' => 4, ); $converter->addNetworks('sxNet', 'ip', 0, 1, $data); $errors = $converter->getErrors(); if (!$errors) { $converter->create($dbFile); } else { print_r($errors); }
スクリプトを実行して待機します。
$ php convert.php
DBサイズの比較
$ ls -l *.dat ... -rw-r--r-- 1 www www 13435116 Jun 30 15:46 SxGeoCity.dat -rw-r--r-- 1 www www 33190825 Oct 12 06:40 iptool.sxgeo.city.dat ...
IPToolデータベースのボリュームは3倍以上です(これはプラスではありません)
<?php require_once(__DIR__.DIRECTORY_SEPARATOR.'Iptool.php'); require_once(__DIR__.DIRECTORY_SEPARATOR.'SxGeo.php'); $dbFile = __DIR__.DIRECTORY_SEPARATOR.'iptool.sxgeo.city.dat'; $iptool = new \Ddrv\Iptool\Iptool($dbFile); $sxgeo = new SxGeo( __DIR__.DIRECTORY_SEPARATOR.'SxGeoCity.dat',2); /* */ $ips = []; for ($i=0;$i<100;$i++) { $ipa = []; for($octet = 0;$octet<4;$octet++) { $ipa[] = rand(0,255); } $ip = implode('.',$ipa); $ips[] = $ip; } /* IPTool */ $res = []; $t1 = microtime(true); foreach ($ips as $ip) { $res[] = $iptool->find($ip); } $t2 = microtime(true); echo 'IP Tool : '.($t2-$t1).PHP_EOL; /* SxGeo */ $res = []; $t1 = microtime(true); foreach ($ips as $ip) { $res[] = $sxgeo->getCityFull($ip); } $t2 = microtime(true); echo 'SxGeo : '.($t2-$t1).PHP_EOL;
比較速度テストiptool-1.0.6およびSxGeo-2.2.3
$ php compare.php
100アドレスでの3つのテストの結果
IP Tool : 0.026905059814453 SxGeo : 0.031632900238037 IP Tool : 0.025413036346436 SxGeo : 0.023004055023193 IP Tool : 0.016932010650635 SxGeo : 0.022341012954712
1つのアドレスでの3つのテストの結果
IP Tool : 0.0013048648834229 SxGeo : 0.00016021728515625 IP Tool : 0.00047779083251953 SxGeo : 0.00011301040649414 IP Tool : 0.00046205520629883 SxGeo : 0.00035595893859863
UPD2。 バージョン1.0.7では、検索アルゴリズムはバイナリ検索に変換されます
比較速度テストiptool-1.0.7およびSxGeo-2.2.3
$ php compare.php
100アドレスでの3つのテストの結果
IP Tool : 0.012892961502075 SxGeo : 0.033740043640137 IP Tool : 0.0073931217193604 SxGeo : 0.032436847686768 IP Tool : 0.0043089389801025 SxGeo : 0.028012990951538
1つのアドレスでの3つのテストの結果
IP Tool : 0.0011000633239746 SxGeo : 0.0009000301361084 IP Tool : 0.00040006637573242 SxGeo : 0.00079989433288574 IP Tool : 0.00030016899108887 SxGeo : 0.00020003318786621
おわりに
データベースのサイズで作業する必要があります。
- ディレクトリ間のリンクを実装すると、範囲ベースのサイズが大幅に縮小されます。
- 同じデータの接着間隔(このデータベースにはそのようなデータはありませんが、それらはそのままSxGeoから取得されます);
- SxGeoのように、範囲の開始アドレスを3バイトの形式で保存します。
UPD 3
一部のプロジェクトでは、コンバーターが不要で(データベースは1つのプロジェクトで生成され、他のプロジェクトで複製されます)、プロジェクトに追加の依存関係が追加されました(pdo_sqlite) この点で、ライブラリを2つのプロジェクトに分割することが決定されました。 まあ、ノイズに名前空間を変更しました。
現在、プロジェクトはここにあります。
GitHub Packagist Baseの作成
GitHub Packagistデータベースを検索