ホームページ  >  記事  >  バックエンド開発  >  IP アドレスクエリにおける PHP 二分法の適用_PHP チュートリアル

IP アドレスクエリにおける PHP 二分法の適用_PHP チュートリアル

WBOY
WBOYオリジナル
2016-07-21 15:50:36810ブラウズ

データベースには約数十万の IP レコードが保存されており、レコード セットは次のとおりです:


+----------+----------+----- ----- --+--------+--------+----------+--------+
| ip_end | 国_id | 市_id |
+----------+-----------+- ----- ---+------+--------+------+ 0 | 0 | | 0 | 33554432 | 50331646 | 3 3 64 | 67829759 | 0 |
+----------+----------+-----------+- --------+- --------+--------+--------+
このクエリを実行するには、次の SQL を使用する必要があります:
< ?php
$sql = 'SELECT * FROM i_m_ip WHERE ip_begin <= $client_ip AND ip_end >= $client_ip';
?>
このような検索はインデックスを使用しないので、たとえ使用されたとしても、MySQL クエリ効率は高くありません。 1 秒あたり 500 回を超える可能性が高く、同時実行の最適化を数多く行ってきましたが、最終的な平均クエリ効率は 1 秒あたり約 200 回にすぎず、これは本当に頭の痛い問題です。当初はInnocence IPライブラリの検索手法を利用することも考えましたが、アルゴリズムにずっと抵抗があり、二分法は難しいと思っていたため、利用するまでは至りませんでした。仕方なく、結局二分法でIPアドレスを取得する方法を実現しました。
上の表から、IP ライブラリは 0 から 4294967295 までの連続値であることがわかります。この値を分離して保存すると、数百ギガバイトのデータが存在するため、インデックスを使用する方法がありません。ハッシュする。最終的に、データベースの検索はやめて、PHP を使用してこれらをバイナリ ストレージに変換しました。 IP の開始と終了の長さが 4 バイトの長整数であることがわかります。次の国 ID、州 ID などは 2 バイトの短整数を使用して格納できます。データは 1 行に合計 18 個あります。バイト、合計31万個のデータ、合計わずか5Mです。具体的な IP ライブラリ生成コードは次のとおりです:
/*
IP ファイル形式:
3741319168 3758096383 182 0 0 0 0
3758096384 3774873599 3 0 0 0 0
37748736 00 4026531839 182 0 0 0 0
4026531840 4278190079 182 0 0 0 0
4294967040 4294967295 312 0 0 0 0
*/
set_time_limit(0);
$fp = fopen("./ ip.dat ", 'ab');
if ($handle) {
while (!feof($handle)) {
$buffer = fgets($handle);
$buffer = トリム($buffer);
$バッファ =explode ("t", $buffer);
foreach ($buffer as $key => $value) {
$buffer[$key] = (float)rim($value);
$str = Pack( ' L', $buffer[0]);
$str .= パック('L', $buffer[1]);
$str .= パック('S', $buffer[2]); . = パック('S', $buffer[3]);
$str .= パック('S', $buffer[4]); ; $ Str. = Pack ('s', $buffer [6]); なので、バイナリメソッドを使用するのが簡単です。情報:
function getip($ip, $fp) {
fseek($fp, 0);
$end = filesize('./ip.dat');
$begin_ip = implode(' ', unpack('L', fread($fp, 4)));
fseek($fp, $end - 14);
$end_ip = implode(' ', unpack('L', fread($fp, 4))) ', fread($fp, 2)));
('', unpack('S', fread($fp, 2)));
info[4] = implode('', unpack('S' 、 fread($fp, 2)));


$middle_seek = ceil((($end - $begin) / 18) / 2) * 18 + $begin; $middle_seek _seek , unpack(' L', fread($fp, 4)); middle_ip = sprintf('%u', $middle_ip);

if ($ip >= $middle_ip) {
$begin = $middle_seek; } Else {e $ END = $MIDDLE_SEEK; );
} $ fp 上の

は、IP.DAT ファイル ハンドルを開くためのものです。これは、ファイルを 1 回開くために、30W 行のデータを循環するだけです。正確な IP 情報を見つけるには、最大 7 回 (2^7) 回かかります。当初、取得を高速化するために ip.dat をメモリに置きたかったのですが、後で文字列位置決め関数の効率がファイル ポインターのオフセット位置決めと同じ桁ではないことがわかりました。 IP ライブラリを保存するためにメモリを使用します。
この実装により、IP 検索効率が 100 倍近く向上しました。それ以降、WEB アプリケーションではアルゴリズムは重要ではないという概念は完全に払拭されました。実際、これを達成するために、私も最初は純粋な形式で IP ライブラリを生成し、Discuz の IP クエリ機能を使用してそれを検索するのを手伝ってほしいと Jinhu にアドバイスを求めましたが、彼は私を助けることを拒否しました。そして最終的に私のこの実践と学習を作成しました。場合によっては、自分自身に求めるよりも、他人に求める方が良い場合があります。




http://www.bkjia.com/PHPjc/319312.html

www.bkjia.com

tru​​e

http://www.bkjia.com/PHPjc/319312.html

データベースにはおそらく数十万の IP レコードが保存されており、レコード セットは次のとおりです: +----------+----------+-------- ----+ ---------+-----------+--------+--------+ |ip_begin|ip_end|country_id| prov_id|city_id.. .
声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。