- 浏览: 910471 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
wangzhen199009:
good. Thx for your sharing.
REST和认证 HMAC -
jsshizhan:
你的这个SQL有问题的
数据库中如何使用SQL查询连续数字并且统计连续个数 -
maoghj:
dddddddddddddd
mysql 更改my.cnf 慢查询日志 -
zhoutong123a:
人的贪婪无止境,只能控制,不能满足
招人心得 -
xuerThinkVickie:
...
ZeroClipboard支持IE,firefox,Chrome复制到剪贴板
二分法在IP地址查询中的应用
2007年10月03日 13时55分
本站文章如未特殊注明,均为原创,严禁转载。
前段时间做数据分析,需要大量的IP地址查询(每秒钟近万次检索),首先考虑到使用数据库。数据库大概存储几十万条IP记录,记录集如下:
+----------+----------+------------+---------+---------+--------+--------+
| ip_begin | ip_end | country_id | prov_id | city_id | isp_id | netbar |
+----------+----------+------------+---------+---------+--------+--------+
| 0 | 16777215 | 2 | 0 | 0 | 0 | 0 |
| 16777216 | 33554431 | 2 | 0 | 0 | 0 | 0 |
| 33554432 | 50331647 | 2 | 0 | 0 | 0 | 0 |
| 50331648 | 67108863 | 3 | 0 | 0 | 0 | 0 |
| 67108864 | 67829759 | 3 | 0 | 0 | 0 | 0 |
+----------+----------+------------+---------+---------+--------+--------+
这样做查询需要用到如下SQL:
<?php
$sql = 'SELECT * FROM i_m_ip WHERE ip_begin <= $client_ip AND ip_end >= $client_ip';
?>
这样的检索显然用不到索引,即使用到,MySQL查询效率也不大可能达到每秒500次以上,我做了很多并发优化,最终平均查询效率也只有每秒200次左右,实在是头痛。一开始我也有想到借鉴纯真IP库的检索方法,但是我一直对算法有抵触,也以为二分法很难,所以就没有尝试使用,直到最后没有办法了,才最终实现了二分法的IP地址检索。
从上表可以看到IP库是从0到4294967295的一个连续数值,这个数值要是拆开存储,会有几百G的数据,所以没办法使用索引也没办法哈希。最终我使用PHP将这些东东转为二进制存储,抛弃了数据库的检索。可以看到IP起止长度为一个4字节的长整型,后面的国家ID、省份ID等,可以使用2个字节的短整型来存储,总共一行数据就有18个字节,总共31万条数据,算起来也就5M的样子。具体IP库生成代码如下:
<?php
/*
IP文件格式:
3741319168 3758096383 182 0 0 0 0
3758096384 3774873599 3 0 0 0 0
3774873600 4026531839 182 0 0 0 0
4026531840 4278190079 182 0 0 0 0
4294967040 4294967295 312 0 0 0 0
*/
set_time_limit(0);
$handle = fopen('./ip.txt', 'rb');
$fp = fopen("./ip.dat", 'ab');
if ($handle) {
while (!feof($handle)) {
$buffer = fgets($handle);
$buffer = trim($buffer);
$buffer = explode("\t", $buffer);
foreach ($buffer as $key => $value) {
$buffer[$key] = (float) trim($value);
}
$str = pack('L', $buffer[0]);
$str .= pack('L', $buffer[1]);
$str .= pack('S', $buffer[2]);
$str .= pack('S', $buffer[3]);
$str .= pack('S', $buffer[4]);
$str .= pack('S', $buffer[5]);
$str .= pack('S', $buffer[6]);
fwrite($fp, $str);
}
}
?>
这样IP就按照顺序每18字节一个单位排列了,所以很容易就使用二分法来检索出IP信息:
function getip($ip, $fp) {
fseek($fp, 0);
$begin = 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)));
$begin_ip = sprintf('%u', $begin_ip);
$end_ip = sprintf('%u', $end_ip);
do {
if ($end - $begin <= 18) {
fseek($fp, $begin +;
$info = array();
$info[0] = implode('', unpack('S', fread($fp, 2)));
$info[1] = implode('', unpack('S', fread($fp, 2)));
$info[2] = implode('', unpack('S', fread($fp, 2)));
$info[3] = implode('', unpack('S', fread($fp, 2)));
$info[4] = implode('', unpack('S', fread($fp, 2)));
return $info;
}
$middle_seek = ceil((($end - $begin) / 18) / 2) * 18 + $begin;
fseek($fp, $middle_seek);
$middle_ip = implode('', unpack('L', fread($fp, 4)));
$middle_ip = sprintf('%u', $middle_ip);
if ($ip >= $middle_ip) {
$begin = $middle_seek;
} else {
$end = $middle_seek;
}
} while (true);
}
以上$fp为打开ip.dat的文件句柄,由于是循环检索,所以写在函数外面,免得每次检索都要打开一次文件,30W行数据二分法最多也只需要循环7次(2^7)左右即可找到准确的IP信息。之后本来还想将ip.dat放在内存中加快检索速度,后来发现,字符串定位函数的效率,根本和文件指针的偏移定位不是在一个数量级的,所以还是放弃使用内存来存放IP库。
这个实现,使IP检索效率提高了近百倍,只是一个简单的二分法的应用,从此算法在WEB应用中不重要的观念彻底打消了。其实要实现这个,我还请教了金狐,我一开始是请他帮我生成一个纯真格式的IP库,然后用Discuz的IP查询函数来检索,不过他不肯帮我,最后造就了我的这个实践和学习。有时候,求人不如求己。
陈毅鑫 发表于 PHP程序设计
5 条评论
chris
2008年06月04日 21时46分
你好!
我参考你的文章,也作了一个ip查询的web程序
每次http访问都要fopen ip库一次
我用ab测试了下,每秒只能接受400左右的查询
是不是比你的程序差很多?能不能指点一下
深空
2008年05月12日 19时20分
嗯当时算错了,应该要18次左右。
家蚕
2008年05月12日 13时06分
30W行数据二分法最多也只需要循环7次(2^7)左右即可找到准确的IP信息
-----
7次? 不止吧
李云帆
2008年03月10日 17时00分
这个是个方法,建议再对数据文件分段,提高文件读取效率
神仙
2007年10月04日 16时31分
谁说不能hash了?
难道就非要把整个整数一起hash么
2007年10月03日 13时55分
本站文章如未特殊注明,均为原创,严禁转载。
前段时间做数据分析,需要大量的IP地址查询(每秒钟近万次检索),首先考虑到使用数据库。数据库大概存储几十万条IP记录,记录集如下:
+----------+----------+------------+---------+---------+--------+--------+
| ip_begin | ip_end | country_id | prov_id | city_id | isp_id | netbar |
+----------+----------+------------+---------+---------+--------+--------+
| 0 | 16777215 | 2 | 0 | 0 | 0 | 0 |
| 16777216 | 33554431 | 2 | 0 | 0 | 0 | 0 |
| 33554432 | 50331647 | 2 | 0 | 0 | 0 | 0 |
| 50331648 | 67108863 | 3 | 0 | 0 | 0 | 0 |
| 67108864 | 67829759 | 3 | 0 | 0 | 0 | 0 |
+----------+----------+------------+---------+---------+--------+--------+
这样做查询需要用到如下SQL:
<?php
$sql = 'SELECT * FROM i_m_ip WHERE ip_begin <= $client_ip AND ip_end >= $client_ip';
?>
这样的检索显然用不到索引,即使用到,MySQL查询效率也不大可能达到每秒500次以上,我做了很多并发优化,最终平均查询效率也只有每秒200次左右,实在是头痛。一开始我也有想到借鉴纯真IP库的检索方法,但是我一直对算法有抵触,也以为二分法很难,所以就没有尝试使用,直到最后没有办法了,才最终实现了二分法的IP地址检索。
从上表可以看到IP库是从0到4294967295的一个连续数值,这个数值要是拆开存储,会有几百G的数据,所以没办法使用索引也没办法哈希。最终我使用PHP将这些东东转为二进制存储,抛弃了数据库的检索。可以看到IP起止长度为一个4字节的长整型,后面的国家ID、省份ID等,可以使用2个字节的短整型来存储,总共一行数据就有18个字节,总共31万条数据,算起来也就5M的样子。具体IP库生成代码如下:
<?php
/*
IP文件格式:
3741319168 3758096383 182 0 0 0 0
3758096384 3774873599 3 0 0 0 0
3774873600 4026531839 182 0 0 0 0
4026531840 4278190079 182 0 0 0 0
4294967040 4294967295 312 0 0 0 0
*/
set_time_limit(0);
$handle = fopen('./ip.txt', 'rb');
$fp = fopen("./ip.dat", 'ab');
if ($handle) {
while (!feof($handle)) {
$buffer = fgets($handle);
$buffer = trim($buffer);
$buffer = explode("\t", $buffer);
foreach ($buffer as $key => $value) {
$buffer[$key] = (float) trim($value);
}
$str = pack('L', $buffer[0]);
$str .= pack('L', $buffer[1]);
$str .= pack('S', $buffer[2]);
$str .= pack('S', $buffer[3]);
$str .= pack('S', $buffer[4]);
$str .= pack('S', $buffer[5]);
$str .= pack('S', $buffer[6]);
fwrite($fp, $str);
}
}
?>
这样IP就按照顺序每18字节一个单位排列了,所以很容易就使用二分法来检索出IP信息:
function getip($ip, $fp) {
fseek($fp, 0);
$begin = 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)));
$begin_ip = sprintf('%u', $begin_ip);
$end_ip = sprintf('%u', $end_ip);
do {
if ($end - $begin <= 18) {
fseek($fp, $begin +;
$info = array();
$info[0] = implode('', unpack('S', fread($fp, 2)));
$info[1] = implode('', unpack('S', fread($fp, 2)));
$info[2] = implode('', unpack('S', fread($fp, 2)));
$info[3] = implode('', unpack('S', fread($fp, 2)));
$info[4] = implode('', unpack('S', fread($fp, 2)));
return $info;
}
$middle_seek = ceil((($end - $begin) / 18) / 2) * 18 + $begin;
fseek($fp, $middle_seek);
$middle_ip = implode('', unpack('L', fread($fp, 4)));
$middle_ip = sprintf('%u', $middle_ip);
if ($ip >= $middle_ip) {
$begin = $middle_seek;
} else {
$end = $middle_seek;
}
} while (true);
}
以上$fp为打开ip.dat的文件句柄,由于是循环检索,所以写在函数外面,免得每次检索都要打开一次文件,30W行数据二分法最多也只需要循环7次(2^7)左右即可找到准确的IP信息。之后本来还想将ip.dat放在内存中加快检索速度,后来发现,字符串定位函数的效率,根本和文件指针的偏移定位不是在一个数量级的,所以还是放弃使用内存来存放IP库。
这个实现,使IP检索效率提高了近百倍,只是一个简单的二分法的应用,从此算法在WEB应用中不重要的观念彻底打消了。其实要实现这个,我还请教了金狐,我一开始是请他帮我生成一个纯真格式的IP库,然后用Discuz的IP查询函数来检索,不过他不肯帮我,最后造就了我的这个实践和学习。有时候,求人不如求己。
陈毅鑫 发表于 PHP程序设计
5 条评论
chris
2008年06月04日 21时46分
你好!
我参考你的文章,也作了一个ip查询的web程序
每次http访问都要fopen ip库一次
我用ab测试了下,每秒只能接受400左右的查询
是不是比你的程序差很多?能不能指点一下
深空
2008年05月12日 19时20分
嗯当时算错了,应该要18次左右。
家蚕
2008年05月12日 13时06分
30W行数据二分法最多也只需要循环7次(2^7)左右即可找到准确的IP信息
-----
7次? 不止吧
李云帆
2008年03月10日 17时00分
这个是个方法,建议再对数据文件分段,提高文件读取效率
神仙
2007年10月04日 16时31分
谁说不能hash了?
难道就非要把整个整数一起hash么
发表评论
-
php学习网站
2011-04-25 09:51 1087http://www.php100.com/ -
计划高效的工作
2010-11-09 10:55 951计划高效的工作 打印机为什么卡纸,所以就要有人盯着,不能卡纸 ... -
php编码规范
2010-11-03 22:12 1231B.4. 编码风格 B.4. 编码风格 ¶ B.4.1. PH ... -
一些鲜为人知的编程事实
2010-09-05 18:45 959我的程序员经历让我明 ... -
apache 日志分析软件 Weg Log Explorer
2010-09-04 10:54 1031apache 日志分析软件 Weg Log Explorer -
symfony 和 thinkphp
2010-09-02 17:43 1895symfony 和 thinkphp 配置 需要 ... -
PHP中SQL注入与跨站攻击的防范
2010-07-27 16:49 2986SQL injection即SQL注入是我们每个WEB程序 ... -
接口也是为了方便团队开发上的协同工作!
2010-07-27 12:47 1003接口也是为了方便团队开发上的协同工作! 解耦 // 接口定义 ... -
vi apache 日志脚本 查询统计
2010-06-25 17:15 1187cat wap.snjifen.com-access_log. ... -
chown apache:apache
2010-06-25 09:36 1191drwxr-xr-x 9 root root 4096 ... -
递归删除.svn
2010-06-24 17:17 907引用递归删除.svn -
IE在对iframe里面的页面写Cookie的时候有一些安全限制,导致读写Cookie不成功,解决办法
2010-06-22 11:28 1889IE在对iframe里面的页面写Cookie的时候有一些安全限 ... -
VI 编辑器查询删除
2010-05-14 19:20 1172:g/^,/d :g/fail/d ":g&qu ... -
crontab
2010-04-23 12:47 1030#########自动执行一定要写全路径########### ... -
linux tar 解压缩
2010-04-23 09:58 941tar -zcvf test.tar.gz www/* 压缩 ... -
copy 目录
2010-04-21 17:57 897cp -r -
重启apache
2010-04-20 17:21 953/usr/local/apache2/bin/apachect ... -
配置UCHOME 出现不支持简短标签写法 short_open_tag
2010-04-20 17:07 1475gettpl('header');?>错误 把PHP. ... -
改变表结构字符集
2010-04-13 18:26 1053Alter TABLE joy_winner CONVERT ... -
mysql错误:Table XXX is marked as crashed and should be repaired[转]
2010-04-13 10:53 8532一日正在上班,朋友的QQ图标就激烈的闪亮起来,一看,原来是论坛 ...
相关推荐
总结来说,通过二分法在IP地址查询中的应用,可以有效解决传统数据库在大规模和高频次查询中所面临的性能问题。通过优化数据的存储格式和检索算法,实现了更快的查询响应,满足了数据分析中对于高速度的要求。这种...
当需要查询某个IP地址时,通过二分法在预处理好的数据库中快速找到对应的位置信息。这种设计使得即使在大数据量的IP查询下,也能保持高效的性能。 三、主要功能 1. **快速定位**:ip2region能在微秒级别完成IP地址...
此外,由于IP地址分配可能发生变化,可能会出现查询结果不准确的情况,因此在设计系统时,需要考虑到这种情况并作出相应的错误处理。 总的来说,IP2Region.xdb是一个强大的工具,它以高效的方式解决了IP地址与地理...
《QQ IP数据库读取unit和示例代码》的说明: 在此unit中,实现了对于纯真网络(www.cz88.net)的IP数据库的读取,该数据库是在搜捕的基础上实现的,现广泛应用于QQ和各种论坛上,主要目的是获取IP地址和所在区域的...
在此unit中,实现了对于纯真网络(www.cz88.net)的IP数据库的读取,该数据库是在搜捕的基础上实现的,现广泛应用于QQ和各种论坛上,主要目的是获取IP地址和所在区域的对应关系。 本unit中,对于所有的输入参数没有...
易语言读取QQWary地理位置方法的知识点主要集中在如何利用易语言(一种中文编程语言)来查询IP地址...开发者在实际应用中,需要遵循易语言的语法规则,并合理运用数据库文件及二分法等算法来实现高效稳定的查询服务。
6. 域名和IP地址:在Internet中访问主机可以使用主机名或IP地址,IP地址分为A、B、C、D、E五类,其中200.201.202.203是一个C类IP地址。 7. 插入Word公式:在Word中插入数学公式,可以使用“插入”菜单下的“对象”...
7. **IP 地址**:问题7关于IP地址的描述,错误的是IP地址由32位十进制数组成,实际上IP地址通常表示为点分十进制形式,每段8位,共4段。 8. **数据库表结构**:问题8指出表由字段和记录组成,这是数据库中的基本...
- 在最理想的情况下,数据索引的查询效率接近于二分法查询,即时间复杂度约为O(log N),这比线性搜索的时间复杂度O(N)显著降低。 - **理解数据索引结构:** - 数据索引通常采用B树(B-tree)结构,默认情况下...
该部分内容涉及到计算机科学及技术领域的多个知识点,涵盖的主题包括计算机应用领域、高级程序设计语言、网络技术、域名和IP地址、加密体制、计算机病毒、数据结构、操作系统、进程状态转换、二叉树的数据结构等。...
因此,在有序线性表中可以使用二分法进行查找,但线性链表、二叉链表以及有序线性链表都无法直接使用二分法。 3. 在 Word 文档中,红色波浪下划线通常表示可能存在拼写错误,这是 Word 自带的拼写检查功能。 4. ...
13. **UDP通信获取IP地址**:在使用UDP(User Datagram Protocol)进行通信时,DatagramPacket的getAddress()方法可以获取发送方的IP地址。 14. **Java字节码文件**:Java源代码编译后生成的字节码文件扩展名为....
例如,在需要高效率的ip地址反查场景中,使用传统数据库的between操作无法有效利用索引,导致效率低下。通过一次性排序和折半查找的方式,可以显著提高查询性能,满足每秒1000次以上的查询需求。 对于需要快速检索...
4. **域名与IP地址** - 在Internet中访问主机可以使用主机名或IP地址;200.201.202.203是一个C类IP地址;IP地址是分层结构;主机名与IP地址一一对应。 5. **加密体制** - 明文空间、密文空间、密钥空间、加密算法和...
13. IP地址获取:在Java中,获取InetAddress对象的IP地址通常使用`getHostAddress()`方法。 14. 软件开发周期:在结构化方法中,软件功能分解属于需求分析阶段之后、详细设计阶段之前的总体设计阶段。 15. 变量...
6. IP地址和E-mail地址在网络中是独一无二的,用于标识网络中的特定设备和通信。 7. 创建BufferedReader对象以读取文件时,路径应使用反斜杠(\)且需要转义,因此正确路径是"C:\\my\\1.txt"。 8. 计算机存储程序和...
4. IP地址:IP地址是由32位二进制数组成,通常以四段十进制表示,每段范围是0-255,并且在Internet上是唯一的。 5. 数学运算:`Int()`函数用于取整,`round()`函数用于四舍五入。`Int(-3.1)`是-3,`round(-4.6)`是-...
### 网络协议考试知识点解析 #### 一、网络体系结构边界 网络体系结构边界在...通过深入理解这些知识点,考生将能更全面地掌握网络协议及其在现代通信网络中的应用,为后续的网络设计、分析与优化奠定坚实的基础。
25. IP地址通常用四个十进制数表示,中间用点分隔。 26. C++程序总是从main函数开始执行。 以上是对题目涉及知识点的详细解析,涵盖了计算机基础知识的多个方面,有助于理解计算机二级考试中的常见考点。