使用Redis有序集合搭建自有IP定位解析库(纯真库)
2017-05-11 23:09
447 查看
网上关于IP地理位置解析的API有很多种,如IP138、百度IP定位API等,接入方便,使用简单,而且免费。缺点就是有访问次数限制。
所以有必要搭建自有的IP定位解析库。纯真库是一个比较有名的IP库,而且会不断更新,文章发布时的最新版本是2017-04-25。
下载纯真库并安装到本地,找到安装目录中提取出“qqwry.dat”
下载IPLook工具,将“qqwry.dat”文件转换成txt文本文件
利用数据库工具Navicat将上一步的txt文本文件导入到MySQL,表结构如下
我们都知道,IP(IPv4)共有2^32=4294967296个(除去私有网段、网络ID、广播ID、保留网段、本地环回127.0.0.0网段、组播224.0.0.0网段、实际可用就是25.68亿)。这些IP,每个相邻区间,地理位置是相同的,如218.107.30.50-218.107.30.60,都是属于广东省广州市。纯真库正是利用这个特性,将相邻区间且地理位置相同的IP放在一起,将上亿个IP浓缩成40多万的区间。查找某个IP时,只需要找到IP所在区间,就能获取到相应的位置。
IPv4是由32位二进制数表示,一般的书写法为4个用小数点分开的十进制数。由于需要对区间进行比较,必需将IP转成不带“.”的长整型。
查询的方法有多种
1、网上也有人直接读取解析dat文件,Java、PHP等语言都有相关的算法。dat文件比较大,每个IP进来都需要计算偏移量得出所在区间,性能并不高,非常耗CPU(3个线程各自循环1000次CPU上升到100%)。显然个方案不能放到生产环境去。
2、在数据库中计算区间,SQL为:
由于涉及到区间计算,即使加上了索引,性能也不高。
3、利用方法2,将42亿个IP全解析出来,放到新的表,查找时用“=”,可直接利用索引。这个想法很美好,但是却无法执行,42亿的表,得花多长的时间才将所有IP写进表里,后期也不好维护。
上面的3种方案,显然比较靠普的还是第2种,既然使用MySQL性能不高,那就换成Reids,直接从内存读取。Redis能支持有序集合并支持区间查找,即zRange。
将每个IP区间的结束IP(EndIP)当作score值,value中包含区间的起始IP(StartIP)和位置信息(Country)
经过试验,将MySQL中40多W的IP区间写到Redis中,只需不到10秒。
每个IP区间之间不存在交集,每个查找只要按score查找区间
有3个区间
Value Score
36 60
61 67
68 68
查找IP=50时,查找的score为[50, 50 + N],且要满足50 + N >= 60,否则就无法找到[36, 60]这个区间。
代码如下:
此时,另外一个问题来了,由于N太大,$rows的条数会很多,造成内存浪费,需要限制返回的记录数。由于score是有序且递增的,所以每次返回数组肯定会命中索引为0的那条记录,即每次只要返回一条记录即可。好在Redis提供了limit这个参数,所以将上面的代码进行改造:
这个方案,从dat转txt,txt转DB,DB转Redis前后花了不到10分钟,性能还比前面任意一种方法都高。
线上取10000个真实用户IP进行测试,平均耗时:0.28ms,最长耗时:3ms。
博客原文:http://www.lbog.cn/blog/20
所以有必要搭建自有的IP定位解析库。纯真库是一个比较有名的IP库,而且会不断更新,文章发布时的最新版本是2017-04-25。
一、数据源准备
下载纯真库并安装到本地,找到安装目录中提取出“qqwry.dat”下载IPLook工具,将“qqwry.dat”文件转换成txt文本文件
利用数据库工具Navicat将上一步的txt文本文件导入到MySQL,表结构如下
二、原理
我们都知道,IP(IPv4)共有2^32=4294967296个(除去私有网段、网络ID、广播ID、保留网段、本地环回127.0.0.0网段、组播224.0.0.0网段、实际可用就是25.68亿)。这些IP,每个相邻区间,地理位置是相同的,如218.107.30.50-218.107.30.60,都是属于广东省广州市。纯真库正是利用这个特性,将相邻区间且地理位置相同的IP放在一起,将上亿个IP浓缩成40多万的区间。查找某个IP时,只需要找到IP所在区间,就能获取到相应的位置。IPv4是由32位二进制数表示,一般的书写法为4个用小数点分开的十进制数。由于需要对区间进行比较,必需将IP转成不带“.”的长整型。
三、查询
查询的方法有多种1、网上也有人直接读取解析dat文件,Java、PHP等语言都有相关的算法。dat文件比较大,每个IP进来都需要计算偏移量得出所在区间,性能并不高,非常耗CPU(3个线程各自循环1000次CPU上升到100%)。显然个方案不能放到生产环境去。
2、在数据库中计算区间,SQL为:
SELECT * FROM ip WHERE INET_ATON('218.107.30.50') BETWEEN StartIP AND EndIP LIMIT 1;
由于涉及到区间计算,即使加上了索引,性能也不高。
3、利用方法2,将42亿个IP全解析出来,放到新的表,查找时用“=”,可直接利用索引。这个想法很美好,但是却无法执行,42亿的表,得花多长的时间才将所有IP写进表里,后期也不好维护。
四、高效查询的解决方案
上面的3种方案,显然比较靠普的还是第2种,既然使用MySQL性能不高,那就换成Reids,直接从内存读取。Redis能支持有序集合并支持区间查找,即zRange。将每个IP区间的结束IP(EndIP)当作score值,value中包含区间的起始IP(StartIP)和位置信息(Country)
经过试验,将MySQL中40多W的IP区间写到Redis中,只需不到10秒。
每个IP区间之间不存在交集,每个查找只要按score查找区间
[ip2long(ip), ip2long(ip) + N],N的值必需足够大,确保结束IP的值大于当前区间的结束IP。这个有点难理解,举个栗子:
有3个区间
Value Score
36 60
61 67
68 68
查找IP=50时,查找的score为[50, 50 + N],且要满足50 + N >= 60,否则就无法找到[36, 60]这个区间。
代码如下:
五、性能测试
线上取10000个真实用户IP进行测试,平均耗时:0.28ms,最长耗时:3ms。博客原文:http://www.lbog.cn/blog/20
相关文章推荐
- 使用redis有序集合sorted set设计高效查询ip所在地
- redis有序集合的使用
- redis:php-redis中有序集合 zset的使用
- Redis源码解析(四):redis之数据类型哈希表、列表、集合和有序集合
- php-redis中有序集合 zset的使用
- 为什么使用 Redis及其产品定位
- Linux下使用netfilter进行IP包解析
- 查看网段内正在使用的IP以及ip定位
- 为什么使用 Redis及其产品定位
- 第四章 集合与泛型 第二课: 有序列表的使用
- 用python从redis的有序集合中一次性删除多个值
- 为什么使用 Redis及其产品定位
- 使用動態IP搭建Exchange 2003企業郵件服務器
- 为什么使用 Redis及其产品定位
- oracle 10G 在windows下有网络情况下安装 断网或者换ip不能使用(报无监听程序或者,监听程序无法解析链接
- 使用百度定位服务实现 GPS+基站+WI-FI+IP混合定位功能
- 为什么使用 Redis及其产品定位
- 纯真ip数据文件php解析类
- 使用纯真IP数据库定位IP地址所在地
- 使用ruby解析纯真IP库(qqwry.dat)