最近有打算给博客做个统计插件,看看都是哪儿来的朋友在看我的博客儿,需要一个详细的IP地址归属地数据库。网上找了个纯真的MySQL版的,30多万条数据占了20M左右的数据库空间,觉得有点浪费数据库空间,而且数据库查询时间太慢,特别是要一次性查多个结果时,更是不得了.最主要的是这种应用场合不适合做成数据库形式的。
所以就准备自己做数据库了,自己也有50000亿条数据的数据文件的设计经验。这才30多万条没什么问题。。。按自己的想法生成了文件,并写了PHP和C语言的访问API,心情好的时候再补写上Java啊,汇编啊什么的API也是挺好的。
PHP API(函数):这个是函数形式的PHP代码,博客版面上是写描述,具体代码在这里:ips.php ip_area.php
<?php /** * IP地址归属地查询 PHP函数版 * @author Hoverlees http://www.hoverlees.com me[at]hoverlees.com * @comment 此功能同时包含C/C++,PHP类,PHP函数,等版本提供下载。 * 数据库文件及相关代码下载地址:http://www.hoverlees.com/blog/?p=906 */ /*对象状态常量*/ define('IPV4_AREA_ERROR_SUCCESS',0); //正常 define('IPV4_AREA_ERROR_OPENFILE',-1); //无法打开文件 define('IPV4_AREA_ERROR_FORMAT',-2); //数据库格式不对 /** * IPv4地址转整数函数,注意本函数不检查IP地址的合法性 * @param $ip IPv4地址 * @return 转换后的整数 */ function ipv4_atol($ip){} /** * 创建IP表对象 * @param $filename 数据库文件名 * @param $index_read_parts 索引划分的块数,如果为1,表示索引全部读入内存(占用大约4M内存空间),如果为2则只需要占用4/2=2M内存临时空间。此值越大,需要的内存缓冲越少,但磁盘的IO次数增加。如果需要循环查询多个IP,建议此值设为1.如果只查询一次可以根据内存要求自行设定。 * @return 用于查询的数据库对象,使用完成后请调用ipv4_area_free释放占用的资源 */ function ipv4_area_create($filename,$index_read_parts=1){} /** * IP址所在地查询 * @param $obj 数据库对象 * @param $ip IP地址,可以为'XXX.XXX.XXX.XXX'格式 * @return 归属地名称 */ function ipv4_area_search($obj,$ip){} /** * 释放IP数据库 * @param $object 由函数ipv4_area_create返回的表对象 */ function ipv4_area_free($object){} ?>
代码调用示例:
<?php include('./ips.php'); //包含测试数组$test_ips,包含1760个需要查询所在地的IP地址 $s=microtime(true); $object=ipv4_area_create('./ip_utf8.dat');//初始化数据库 if($object['status']==IPV4_AREA_ERROR_SUCCESS){ foreach($test_ips as $ip){ //循环查询每一个IP的所在地 $area=ipv4_area_search($object,$ip); echo "IP:{$ip}<BR>AREA:{$area}<BR><BR>\n"; } } ipv4_area_free($object); //释放对象 echo microtime(true)-$s; //当创建时参数index_read_parts=1时,查询1760条IP所在地仅需3.68秒(上网本) ?>
PHP IPArea类,功能都跟上面一样的,不过不同的人习惯不一样呗,咱就多提供一种思路了。完整的代码在这里: IPArea.php
<?php /** * IP地址归属地查询 PHP类版 * @author Hoverlees http://www.hoverlees.com me[at]hoverlees.com * @comment 此功能同时包含C/C++,PHP类,PHP函数,等版本提供下载。 * 数据库文件及相关代码下载地址:http://www.hoverlees.com/blog/?p=906 */ class IPArea{ /*对象状态常量*/ const IPV4_AREA_ERROR_SUCCESS=0;//正常 const IPV4_AREA_ERROR_OPENFILE=-1; //无法打开文件 const IPV4_AREA_ERROR_FORMAT=-2; //数据库格式不对 private $innerObject; /** * 构造函数 * @param $filename 数据库文件名 * @param $index_read_parts 索引划分的块数,如果为1,表示索引全部读入内存(占用大约4M内存空间),如果为2则只需要占用4/2=2M内存临时空间。此值越大,需要的内存缓冲越少,但磁盘的IO次数增加。如果需要循环查询多个IP,建议此值设为1.如果只查询一次可以根据内存要求自行设定。 */ public function IPArea($filename,$index_read_parts=1){} /** * 取得对象状态 * @return 返回状态常量之一 */ public function getStatus(){} /** * 取得数据库的编码 * @return 返回编码,可能是UTF-8或GBK */ public function getDBEncoding(){} /** * IP址所在地查询 * @param $ip IP地址,可以为'XXX.XXX.XXX.XXX'格式 * @return 归属地名称 */ public function search($ip){} /** * 释放对象占用的资源 */ public function release(){ } /** * IPv4地址转整数函数,注意本函数不检查IP地址的合法性 * @param $ip IPv4地址 * @return 转换后的整数 */ public static function atol($ip){} private function getAreaString($section_offset){} } ?>
调用示例代码:
<?php include('./ips.php'); //包含测试数组$test_ips,包含1760个需要查询所在地的IP地址 $ia=new IPArea('./ip_utf8.dat'); if($ia->getStatus()==0){ foreach($test_ips as $ip){ //循环查询每一个IP的所在地 $area=$ia->search($ip); echo "IP:{$ip}<BR>AREA:{$area}<BR><BR>\n"; } } $ia->release(); ?>
接下来是大家期待的C语言代码了,C语言查询1760个IP地址归属地仅需要0.6s。
ip_area.h ip_area.c ips.h
/** * IP地址归属地查询 C/C++版 * @author Hoverlees http://www.hoverlees.com me[at]hoverlees.com * @comment 此功能同时包含C/C++,PHP类,PHP函数,等版本提供下载。 * 数据库文件及相关代码下载地址:http://www.hoverlees.com/blog/?p=906 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #ifndef __HOVERLEES_IP_AREA_H #define __HOVERLEES_IP_AREA_H /*对象状态常量*/ #define IPV4_AREA_ERROR_SUCCESS 0 //正常 #define IPV4_AREA_ERROR_OPENFILE -1 //无法打开文件 #define IPV4_AREA_ERROR_FORMAT -2 //数据库格式不对 typedef struct _IP_AREA_OBJ{ int status; int num_items; int index_pos; int data_pos; int index_read_parts; int each_read_item; int each_read_byte; FILE* fp; char* last_data; char encoding[8]; }IP_AREA_OBJ; /** * IPv4地址转整数函数,注意本函数不检查IP地址的合法性 * @param ip IPv4地址 * @return 转换后的32位整数 */ unsigned int ipv4_atoi(const char* ip); /** * 创建IP表对象 * @param filename 数据库文件名 * @param index_read_parts 索引划分的块数,如果为1,表示索引全部读入内存(占用大约4M内存空间),如果为2则只需要占用4/2=2M内存临时空间。此值越大,需要的内存缓冲越少,但磁盘的IO次数增加。如果需要循环查询多个IP,建议此值设为1.如果只查询一次可以根据内存要求自行设定。 * @return 用于查询的数据库对象,使用完成后请调用ipv4_area_free释放占用的资源 */ IP_AREA_OBJ* ipv4_area_create(const char* filename,int index_read_parts); /** * 释放IP数据库 * @param obj 由函数ipv4_area_create返回的表对象 */ void ipv4_area_free(IP_AREA_OBJ* obj); /** * IP址所在地查询 * @param obj 数据库对象 * @param ip IP地址,可以为'XXX.XXX.XXX.XXX'格式 * @param buf 地址缓存,请确保大于100字节。 * @return buf本身 */ char* ipv4_area_search(IP_AREA_OBJ* obj,const char* ip,char* buf); #endif
C语言调用示例:
#include <stdio.h> #include <stdlib.h> #include "ip_area.h" #include "ips.h" //1760多个IP地址数组 void main(int argc,char* argv[]){ int i; char out[100]; IP_AREA_OBJ* obj; obj=ipv4_area_create("./ip_utf8.dat",1); for(i=0;i<1767;i++){ ipv4_area_search(obj,ips[i],out); printf("IP:%s AREA:%s\n",ips[i],out); } ipv4_area_free(obj); }
有空的时候再传传JAVA的版本上来。如果代码有错误,请指出,我会尽快fix掉。
你好,我是自己写程序组织的数据文件,主要使用了二分查找法建立索引数据. 如果你是数据库查一条IP很慢,可能是你数据库设计的问题.
您好,我现在的一个项目也遇到了这种情况,mysql存放ip库查询太慢了,我想请问您是如何压缩成dat文件的,十分感谢!!!我的联系邮箱xuanmumu@gmail.com
是一个超过100T的静态数据库,自己用B+,二分,等算法散列到多个数据文件就行了.无须数据库.
50000亿条数据?!!能透露下是什么应用吗?不用数据库?
做的不错!赞一个