IP库建设,从IPv6数据聚合说起

导语:  本文主要讲述如何将客户端提供的IPv6数据聚合,从而应用于有IPv6查询需求的业务

数据来源

本文计算所用的数据来自于客户端提供的IPv6-IPv4的双栈数据源,上报的一条日志记录包括一个IPv6和IPv4地址,根据IPv4地址进行查询,可以得到对应的IPv6地址的国家、省份、城市、运营商等重要信息,根据这些地理位置属性,便可以将属性相同的IPv6地址聚合成段。

理论基础

IPv6有128位,其中后64位是接口id,只有前64位参与网络分配。故在IPv6聚合数据时,可以忽略掉后64位,这样可以简化IPv6的数据结构表示,减少后续计算的麻烦。

一般在IP库中,存储的不是单个的ip,而是属性相同的ip段。在IPv4的地址库里,IPv4地址段的基本单位是一个C段,同一个C段内的是地理位置以及运营商信息相同的C类地址,一个IPv4地址段中的IPv4地址前24位是相同的。因此,IPv4的地址库可以通过C段的表示方式,在合并相邻的C段后,可以将四十多亿的IPv4压缩到几百万条的IP段。

对于IPv6来说,因为它采用了类似于CIDR(无分类编址)的实现,所以没有办法按照固定的前缀长度对IPv6地址段进行定义。尽管如此,本文所说IPv6地址的聚合还是基于“同一个IP段的前缀是相同”这个理论,只不过IP段的公共前缀长度不是统一的,我参考了IPv6地址规划与分配 这篇文章,一般用来的分配的IPv6地址段的前缀长度在40位与64位之间,IP段的公共前缀长度越长,IP段的粒度也越细,IP段数量越多,但聚合效果也越差;但如果公共前缀过短,IP段粒度过粗,又无法保证该IP段的精度。对于本文的聚合运算来说,需要结合各种粒度的运算才能得到一些精确的ip段,例如,省份已知的ip段粒度就较粗,可以用较短的公共前缀表示;城市和区县已知的ip段粒度就较细,可以用长一点的公共前缀表示。

整体流程

原始数据存放在hive表中,数据周期为一周;IPv6聚合计算是采用scala编写的spark程序,每周进行一次计算。目前该计算有两种维度,一个是根据(省份+运营商)进行聚合,另外一种是根据(城市+运营商)进行聚合。以下展示的是省份级别的聚合。

在省份级别的聚合中,对于省份已知的IPv6地址,我从N=40开始聚合,即是将前40位前缀相同的IPv6地址归类在一起,得到一个/40的IP段,选出其中出现次数最多的省份,以及该省份内出现次数最多的运营商,用来定义最终这个IP段的省份和运营商。若该省份和运营商在该段内的数据占比越大,说明该属性就越能代表该IP段,该IP段划分得越准确,IP段的精度越高;若该段内夹杂的其他省份和运营商比较多,则说明不能直接定义该IP段,还需要再细分,于是我继续重复以上计算,增大IP段的公共前缀长度N,尝试将前48位前缀相同的ip聚合在一起,得到一系列48/的IP段,继续判断这些段的精度是否够高。。以此类推,从粗粒度往细粒度一步步聚合。通过这样的划分,就能得到一系列的前缀长度不同一的IP段。这些IP段的精度都必须够高,目前我设定的阈值是0.9,即是IP段内90%以上的IP需要有相同的省份和运营商。

接下来,查找有哪些IP段是相邻比较近的,这些段可以尽量合并,从而减少IP段的数量。然后,再把计算得到的不同粒度的IP段合并到一个数组中;最后,再把当次计算的IP段与历史数据中的IP段合并,就能得到最终的IPv6地址段。剩下那些未能归入某个IP段内的单个IPv6地址,会放入累计池中,参与下一周期的计算。

细节处理

1. IP段的具体定义

class IPv6Section {  var startIp:String = ""    //ip段起点ip(16进制字符串)  var endIp:String = ""      //ip段结束ip  var ipinfo:IPInfo = new IPInfo()  //存放IP段的地理位置、运营商信息  var update_time = 0l    //更新时间  var level = 0           //IP段的地理级别,0,1,2代表省份,城市,区县  var accuracy = 0d     //精度  var pointNum = 0l     //ip段内的点数量}

(左滑可查看完整代码)

2. IP段的合并

由于IP库客户端查询ip是采用二分查找的方式,在一个ip段数组中查询ip所处的段,因此ipv6的段也需要“铺平”为数组的形式,在计算之后将不同粒度的ip段合并起来。合并过程中会遇到ip段重叠的情况,这时候就需要根据一些指标,保留一些段和删除一些段。首先,新的ip段优先覆盖旧的ip段,例如本周期计算的ip段会覆盖上一个周期计算得到的ip段;其次,地理级别越高的ip段,会覆盖级别低的ip段,例如深圳市已知的ip段会优先覆盖仅广东省已知的ip段;最后,若地理级别相同,则会比较ip段的精度,精度越高,说明该ip段越可靠,能够优先覆盖精度低的ip段。

3. 数据过期淘汰

实际计算中发现,ipv6的数据比想象中要稀疏,大概有30%~40%左右的ip段只是包含了一条ip记录,换言之仅仅是根据一条ip记录就能得到该ip段,这样无法排除偶然性。为了清洗一些可能不可靠的ip段,每次计算时,都会统计有哪些ip段是有新数据“命中”的,若长时间没有数据记录落到这个ip段中,则说明该段不可靠或者是已经“过期”了,该数据则会被清洗掉。

数据的准确性

目前IPv6地址库共有ip段96万个,省份的精确度在95%左右,城市的精确度在75%左右。这里的精确度指的是:每次计算前,会用当前的ip库查询数据源的每条记录,若一条ipv6-ipv4的记录中,ipv6的查询结果与ipv4的查询结果是一致的,则可以判断是准确的。目前城市级别的精确度不高,是因为按照城市粒度划分的ipv6数据过于稀疏,无法像省份已知的数据那样快速地聚合成一个大段,这方面会在以后进一步改进。

原文发布于微信公众号 - 腾讯技术工程(Tencent_TEG)

原文发表时间:2019-09-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券