业务的要求
- 判断一个IP是不是属于中国的
- 时间复杂度尽量小,适用于请求量大的业务场景
- 不要请求第三方接口,防止出现网络异常
由于只需要判断到国家的纬度,所以不需要用到纯真IP库这样庞大的IP库,通过网上的查询,发现有个中国IP段的列表
http://ipblock.chacuo.net/down/t_txt=c_CN
可以看到IP段有2K个左右,如果用二分查询法,时间复杂度是logN。基本上能够满足需求了。
实现步骤
一、 整理IP段配置
为了方便IP进行比较,这里将IP转换为long格式。
- 把数据load进来,取第一第二行,ip2long处理
- 第一行保存在left,第二行保存在right中
- 然后根据left进行排序
最终得到的结果,如下:
<?php
#部分数据
array (
0 =>
array (
'left' => '16777472',
'right' => '16778239',
),
1 =>
array (
'left' => '16779264',
'right' => '16781311',
),
2 =>
array (
'left' => '16785408',
'right' => '16793599',
),
3 =>
array (
'left' => '16842752',
'right' => '16843007',
),
4 =>
array (
'left' => '16843264',
'right' => '16859135',
)
);
二、二分查找法
function binarySearch(Array $arr, $target) {
$low = 0;
$high = count($arr) - 1;
while($low <= $high) {
$mid = floor(($low + $high) / 2);
#找到元素
if($target>=$arr[$mid]['left'] && $target<=$arr[$mid]['right']){
return true;
}
#中元素比目标大,查找左部
if($target < $arr[$mid]['left'] ){
$high = $mid - 1;
}
#中元素比目标小,查找右部
if($target > $arr[$mid]['left'] ){
$low = $mid + 1;
}
}
#查找失败
return false;
}
bitmap算法是否可行?
ip地址转为long的最大值刚好是2^32-1 ,那么在redis中我们刚好可以申请一个4294967295的bitmap,占用内存为512M。
如果能够把所有中国的IP对应的long都设置为1,那么每次查询都只要0(1),遇到的问题是redis的setbit怎么批量设置呢?如果要一个ip设置一次的话,需要设置3亿次,这个比较难实现。。