Java - ip2region - 基础篇(你知道ip2region吗?)

Java - ip2region - 基础篇(你知道ip2region吗?),第1张

Java - ip2region - 基础篇(你知道ip2region吗?)

本篇主要介绍 ip2region, ip2region 支持很多客户端,本次主要以Java来介绍

在进行系统开发时,我们一般会涉及到获取到用户的具体位置信息,一般有两个方法:

  • 根据GPS 定位的信息 (一般用于手机端)
  • 用户的 IP 地址解析

每个手机都不一定会打开 GPS,而且有时并不太需要太精确的位置(到城市这个级别即可),所以根据 IP 地址入手来分析用户位置是个不错的选择。

下面就介绍一个分析 IP 地址一个比较好的东西 ip2region,这篇介绍一些基础的东西,下篇介绍使用与原理

前言 ip2region 是什么?

官方:ip2region - 准确率99.9%的离线IP地址定位库,0.0x毫秒级查询,ip2region.db数据库只有数MB,提供了java,php,c,python,nodejs,golang,c#等查询绑定和Binary,B树,内存三种查询算法

其实就是一个库,用于 IP地址解析定位用的,支持很多客户端,速度很快也不占内存,目前github已经到了10k star 了

特性:

  • 准确率高,数据聚合多个供应商的数据
  • 体积小,空间优化形成了仅仅几兆的 ip2region.db 文件
  • 速度快,内置三种查询算法,支持毫秒级查询
  • 支持多客户端,支持很多客户端,java、C#、php、c、python、nodejs、php扩展(php5和php7)、golang、rust、lua、lua_c, nginx

它的数据聚合了一些知名ip到地名查询提供商的数据,例如:(如果一下开放API或者数据都不给开放数据时ip2region将停止数据的更新服务)

  • 淘宝IP地址库, http://ip.taobao.com/
  • GeoIP, https://geoip.com/
  • 纯真IP库, http://www.cz88.net/
ip地址相关知识

不知道的去百度百科了解一下 ip地址介绍

简短总结:

IPv4使用32位(4字节)地址,如果存储需要有 2^32 = 4294967296 约42.9 亿个地址空间来存储

关于位置信息,一般由 国家|区域|省份|城市|ISP组成,如:

// ip地址|国家|区域|省份|城市|ISP
0.0.0.0|0|0|0|内网IP|内网IP
0.0.0.1|0|0|0|内网IP|内网IP
...
1.0.15.255|中国|0|广东省|广州市|电信
...
255.255.255.255|0|0|0|内网IP|内网IP

那我们需要根据ip地址获取位置信息,那如果想要直接根据ip地址获取到位置信息,即时间复杂度为O(1) 时,我们可以存储这些位置信息入文件中

假如使用 utf-8 进行存储,一条记录最短的情况是 0.0.0.0|0|0|0|0|0,有17位,占用17个字节,那么我们需要的地址空间为:

17 * 2^32 = 73014444032 B = 71303MB = 71GB 显然,不能够直接这样存储,谁会为了O(1)来存储72G的一个文件呀~

那么如何来又让速度快,又存储空间小呢?

空间文件优化

1、第一种 记录合成

因为 相邻 IP 具有相同的位置信息,所以可以其记录合成一条记录。如下文件数据(地址段依次递增):

//起始ip,结束ip,国家,区域,省份,市,运营商。无数据区域默认为0  根据开始ip地址升序排列

0.0.0.0|0.255.255.255|0|0|0|内网IP|内网IP
...
1.0.8.0|1.0.15.255|中国|0|广东省|广州市|电信
...
224.0.0.0|255.255.255.255|0|0|0|内网IP|内网IP

0.0.0.0|0.255.255.255|0|0|0|内网IP|内网IP
1.0.0.0|1.0.0.255|澳大利亚|0|0|0|0
1.0.1.0|1.0.3.255|中国|0|福建省|福州市|电信
1.0.4.0|1.0.7.255|澳大利亚|0|维多利亚|墨尔本|0
1.0.8.0|1.0.15.255|中国|0|广东省|广州市|电信
1.0.16.0|1.0.31.255|日本|0|0|0|0
1.0.32.0|1.0.63.255|中国|0|广东省|广州市|电信

在ip2region 库中的 ip.merge.txt 就是根据这样的规则来记录所有的IP地址,文件大小仅仅才 39.3 M ,如若根据ip地址查找位置信息,最简单的就是顺序遍历,当该ip在某条记录起始和结束ip之间时,即命中。

2、第一种 存储方式转变

上述的文件存储的ip地址都是以字符串的方式进行存储,比如像最短的字符串 0.0.0.0 占据 7 字节,最长的字符串 111.111.111.111 占据 15 字节

那如果将其转换成整型进行存储便可以大大节省空间,如果转换成整型,0.0.0.0 是 int(0), 111.111.111.111 是 int(1869573999),都仅仅占据 4 字节,可以省略大量的存储空间

3、第三种 位置信息优化

相同的位置信息会对应不同的 IP 段,所以仍有大量的位置信息在 IP 库文件中,我们可以在内存中只保留一份位置信息,并使用指针或者文件偏移量+数据长度来获取对应的位置信息

这个的意思就是也就是如:

|中国|华南|广东省|广州市|电信 

其实这样的数据在文件中被重复存储了很多次,可以只存储一次,靠指针或者文件偏移量+数据长度来获取对应的位置信息

在ip2region 库中的 ip2region.db就是在 ip.merge.txt 基础上,为所有数据生成一份索引,并和数据地址组成一个索引项(index block),,然后按起始ip升序排列组成索引,并存储到数据文件的末尾,最终生成的 ip2region.db文件大小只有3.5M,根据索引获取位置信息,也直接将性能提升到0.0x毫秒级别

ip2region.db 结构介绍

ip2region.db 的文件结构图如下:

根据结构图,可以看到其主要分为以下四个部分:

  • SUPER BLOCK:存储 INDEX 的起始位置(前四个字节) 与 INDEX 的结束位置(后四个字节),用于快速获取 INDEX 索引区域的地址
  • HEADER INDEX: INDEX 区的二级索引 ,一个个 header index block 组成, 每个 header index block 对应存储 INDEX 中的 每个 index partion(4k) (前四个字节存储每个4K分区起始位置的index block 的起始ip值,后四个字节指向该index block的地址)
  • DATA:数据部分 以 城市ip,国家,区域,省份,城市,运营商 形式存储, 例如:2163|中国|华南|广东省|深圳市|鹏博士
  • INDEX:一个个 index partion 组成,对应 index block 的索引分区,每个index block 对应ip.merge.txt中每一条记录的 起始ip,结束ip , 位置数据(前四个字节存储起始ip,中间四个字节存储结束ip, 后四个字节存储数据信息 ,数据信息的 前三个字节保存数据地址(DATA中),后一个字节保存数据长度。)
查询算法

内置了三种查询算法,单次查询都在0.x毫秒级别:

  1. binary算法:基于二分查找,基于ip2region.db文件,不需要载入内存,单次查询在0.x毫秒级别
  2. b-tree算法:基于btree算法,基于ip2region.db文件,不需要载入内存,单词查询在0.x毫秒级别,比binary算法更快
  3. memory算法:整个数据库全部载入内存,单次查询都在0.1x毫秒内,C语言的客户端单次查询在0.00x毫秒级别。

本质:三种方法都是通过不同的方法先找到 index,然后根据二分查找找到 ip地址 处于的 index bolck(12个字节),根据后四个字节存储的数据信息找到数据,然后根据数据信息中的前三个字节找到对应的数据地址

binary搜索

基于二分查询方法,步骤如下:

  • 把 ip值 通过 ip2long 方法转为长整型
  • 通过 SUPER BLOCK 拿到 INDEX 的起始位置和结束位置
  • 两个位置相减,然后 +1 得出index block 总数
  • 采用二分法直接求解,比较 index block 和当前 ip 的大小,即可找到该ip属于的 index block(经典二分)
  • 拿到该 index block 的后面四个字节(数据信息), 分别得到数据长度和数据地址
  • 从数据地址读取拿到的所得长度的字节,即是搜索结果
b-tree 搜索

b-tree 搜索用到了 HEADER INDEX,第一步先在 HEADER INDEX 中搜索,再定位到 INDEX 中的某个 4k index分区搜索。

步骤:

  • 把 ip值 通过 ip2long 转为长整型
  • 使用二分法在 HEADER INDEX 中搜索,比较得到对应的 header index block
  • header index block 指向 INDEX 中的一个 4K 分区,所以直接把搜索范围降低到 4K
  • 采用二分法在获取到的 4K 分区搜索,得到对应的 index block
  • 拿到该 index block 的后面四个字节(数据信息), 分别得到数据长度和数据地址
  • 从数据地址读取拿到的所得长度的字节,即是搜索结果
memory算法

该方法和 binary搜索 方法类似,区别就是它将 ip2region.db 全部读进内存中再进行查找

相关链接

GitHub ip2region

Java - ip2region - 使用篇

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/756661.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-30
下一篇 2022-04-30

发表评论

登录后才能评论

评论列表(0条)

保存