创建一套IP查询系统,将IP对应到地区,实现每秒千次以上的查询。php
将网上的数据源扒下来,一条一条放到mysql中,而后使用sql语句查询,创建索引,
数据库结构
SET FOREIGN_KEY_CHECKS=0;mysql
-- Table structure for ip_address
sql
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', '广东省', '电信');ui
.......url
查询数据的时候使用SELECT * FROM ip_address WHERE ip_startip <= ip AND ip_endip >= ip。
可是不管如何,我都达到不了想要的执行效率,最好的时候每秒查询的数据都超不过30条。.net
完成不了任务,只好去问问度娘。code
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'; }
}ip
用的是二进制的文件,而后使用快速查找法:(折半查找法)。效率一会儿提升了上来,每秒查询效率稳稳过5000次。哈哈!
方式和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扩展哈!)