創建一套IP查詢系統,將IP對應到地區,實現每秒千次以上的查詢。

這兩天接了個任務,描述以下:

創建一套IP查詢系統,將IP對應到地區,實現每秒千次以上的查詢。php

1.MySQL+PHP的方式:

將網上的數據源扒下來,一條一條放到mysql中,而後使用sql語句查詢,創建索引,
數據庫結構
SET FOREIGN_KEY_CHECKS=0;mysql


-- Table structure for ip_addresssql


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

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';
}

}ip

用的是二進制的文件,而後使用快速查找法:(折半查找法)。效率一會兒提升了上來,每秒查詢效率穩穩過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擴展哈!)

相關文章
相關標籤/搜索