您的位置:首页 > 编程语言 > PHP开发

每秒万次查询,PHP实现 IP查询归属地

2014-04-16 18:38 489 查看


这两天接了个任务,描述如下:

建立一套IP查询系统,将IP对应到地区,实现每秒千次以上的查询。


1.MySQL+PHP的方式:

将网上的数据源扒下来,一条一条放到mysql中,然后使用sql语句查询,建立索引,

数据库结构

SET FOREIGN_KEY_CHECKS=0;
-- Table structure for 
ip_address

DROP TABLE IF EXISTS 
ip_address
;

CREATE TABLE 
ip_address
 (
id
 int(11) unsigned NOT NULL AUTO_INCREMENT,
start_ip
 int(10) unsigned NOT NULL,
end_ip
 int(10) unsigned NOT NULL,
region
 varchar(50) NOT NULL,
address
 varchar(100) NOT NULL,

PRIMARY KEY (
id
),

KEY 
start
 (
start_ip
)
USING BTREE,

KEY 
end
 (
end_ip
)

) ENGINE=MyISAM AUTO_INCREMENT=405037 DEFAULT CHARSET=utf8;
INSERT INTO ip_address VALUES ('1', '0', '16777215', 'IANA保留地址', 'CZ88.NET');

INSERT INTO ip_address VALUES ('2', '16777216', '16777471', '澳大利亚', 'CZ88.NET');

INSERT INTO ip_address VALUES ('3', '16777472', '16778239', '福建省', '电信');

INSERT INTO ip_address VALUES ('4', '16778240', '16779263', '澳大利亚', 'CZ88.NET');

INSERT INTO ip_address VALUES ('5', '16779264', '16781311', '广东省', '电信');
.......
查询数据的时候使用SELECT * FROM ip_address WHERE ip_startip <= ip AND ip_endip >= ip。

但是无论如何,我都达到不了想要的执行效率,最好的时候每秒查询的数据都超不过30条。
完成不了任务,只好去问问度娘。


2.diszuz中的实现方式:

function convertip_tiny($ip, $ipdatafile) {
static $fp = NULL, $offset = array(), $index = NULL;

$ipdot = explode('.', $ip);
$ip    = pack('N', ip2long($ip));

$ipdot[0] = (int)$ipdot[0];
$ipdot[1] = (int)$ipdot[1];

if($fp === NULL && $fp = @fopen($ipdatafile, 'rb')) {
$offset = @unpack('Nlen', @fread($fp, 4));
$index  = @fread($fp, $offset['len'] - 4);
} elseif($fp == FALSE) {
return  '- Invalid IP data file';
}

$length = $offset['len'] - 1028;
$start  = @unpack('Vlen', $index[$ipdot[0] * 4] . $index[$ipdot[0] * 4 + 1] . $index[$ipdot[0] * 4 + 2] . $index[$ipdot[0] * 4 + 3]);

for ($start = $start['len'] * 8 + 1024; $start < $length; $start += 8) {

if ($index{$start} . $index{$start + 1} . $index{$start + 2} . $index{$start + 3} >= $ip) {
$index_offset = @unpack('Vlen', $index{$start + 4} . $index{$start + 5} . $index{$start + 6} . "\x0");
$index_length = @unpack('Clen', $index{$start + 7});
break;
}
}

@fseek($fp, $offset['len'] + $index_offset['len'] - 1024);
if($index_length['len']) {
return '- '.@fread($fp, $index_length['len']);
} else {
return '- Unknown';
}

}
用的是二进制的文件,然后使用折半查找法。效率一下子提高了上来,每秒查询效率稳稳过5000次。哈哈!


3.PHP扩展的方式:

方式和PHP一样的,不过使用了C语言,效率直接超过了50000(五万)次。效率那就不用说了,

扩展地址:http://pecl.php.net/package/qqwry

下载后安装方式按照phpize的方式即可

计入安装目录:

./phpize

./configure

make && makeinstall

然后要确保qqwry.so放到扩展目录下,并添加php.ini:

extension=qqwry.so

OK,已经好了
核心代码如下:

static uint32_t search_index(const uint32_t ip,FILE *qqwry_file) {
uint32_t index_ip;
unsigned char head[8];
unsigned char index_bytes[7];
fread(head,8,1,qqwry_file);
uint32_t index_start,index_end,index_mid;
index_start = (uint32_t)LE_32(&head[0]);
index_end = (uint32_t)LE_32(&head[4]);
while (1) {
if ((index_end-index_start)==7) {
break;
}
//printf("index:%u:%u\n",index_start,index_end);
index_mid=index_end/7 - index_start/7;
if (index_mid%2==0) {
index_mid=index_mid/2;
} else {
index_mid=(index_mid+1)/2;
}
index_mid=index_start+index_mid*7;
fseek(qqwry_file,index_mid,SEEK_SET);
fread(index_bytes,7,1,qqwry_file);
index_ip=(uint32_t)LE_32(&index_bytes[0]);
if (index_ip==ip) {
break;
} else if (index_ip<ip) {
index_start=index_mid;
} else {
index_end=index_mid;
}
}
if (index_ip>ip) {
fseek(qqwry_file,index_start,SEEK_SET);
fread(index_bytes,7,1,qqwry_file);
}
return (uint32_t)LE_24(&index_bytes[4]);

}

这也是我目前为止找的效率最快的方式了,完全能够买足公司的需求了。如果有能够效率更高的方式,留个言哈。
所有需要的文件都可以从这里下载哈:
http://url.cn/QorZ2O

下载后把data.rar解压到当前目录,并执行index.php就可以查看后面的两种方式的执行效率了(前提是,有装qqwry扩展哈!)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息