在 SQL Server 中查询 Maxmind GeoLite2 City Blocks csv 以获取 IPv6 和 IPv4 地址的 geoname_id

Posted

技术标签:

【中文标题】在 SQL Server 中查询 Maxmind GeoLite2 City Blocks csv 以获取 IPv6 和 IPv4 地址的 geoname_id【英文标题】:Query Maxmind GeoLite2 City Blocks csv in SQL Server to get geoname_id for IPv6 and IPv4 addresses 【发布时间】:2014-03-30 12:51:48 【问题描述】:

Maxmind 有两个免费的 GeoLite2 cvs 文件,我们可以使用它们来查找与 IP 地址范围相关的位置。我想在 SQL Server 中创建一个存储过程,该过程使用导入的表([GeoLite2_City_Blocks] 和 [GeoLite2_City_Locations]),采用传入 IP 地址的 varchar(45) 字符串并返回与该 IP 地址关联的位置。我原以为网上会有相关的 T-SQL 代码,但我找不到。

我查看了Querying GeoLite2 Country CSV in SQL,但我根本不明白 Prefix_Length(在 Blocks 表中称为 Network_mask_Length)是做什么的。如果它们与 CIDR 前缀相同(请参阅:http://en.wikipedia.org/wiki/IPv6_subnetting_reference),我会更加困惑,因为 Network_mask_Length 可能是 118、119 或该***页面上未显示的许多其他数字。

我看到一个关于 SQL Server 中 IPv6 的网页建议使用零填充进行比较。我想免费的 GeoLite2 不能简单地被查询而不至少在表中添加一个额外的列作为范围的结束 IP 地址?

我想我可以从 IPv4 地址前面去掉“::ffff:”,如果这样说的话是正确的。但是我仍然不知道如何将 network_mask_length (前缀)字段翻译成我理解的内容。然后零填充 IPv6 地址,但我仍然不知道那个前缀是什么。

我的问题是:

    是否有任何可用的示例代码可以让我走上正轨?或者, 我需要做什么来创建一个接受 IPv4 或 IPv6 地址并返回相关位置或 geoname_id 的 proc?

【问题讨论】:

你不应该在其中看到任何长于 /64 的前缀。 @MichaelHampton,这不是真的。尽管对于公共 IPv6 地址,您不太可能看到任何长于 /64 的前缀,但对于 IPv4 映射地址(例如 ::FFFF:1.1.1.1),您会看到更长的前缀。 此外,数据库不是子网到位置的映射,而是IP范围到位置的映射。尽管 IPv6 子网的某些功能会因小于 /64 的子网(例如 SLAAC)而中断,但这在这里并不重要。没有理由不存在设备位置已知的特定 /128 地址的映射。 @oschwald 是的,但您不应该在这个特定的数据库中看到 IPv4 映射的地址。 @MichaelHampton,这就是在alpha GeoLite2 CSVs 中包含 IPv4 地址的方式。您可能会想到旧版 CSV。 【参考方案1】:

我使用这个查询(实际上是它的 mysql)来导入 Block-File,将 IP 地址转换为 from-to ip 范围以执行简单的BETWEEN 查询。

我在表中添加了两个字段:start_ipend_ipstart_ip 填充network_start_ip 的数值,end_ip 填充使用network_mask_length 计算的ip 范围的末尾。正如@oschwald 所说,我从前缀长度中减去了 96 并删除了 ::FFFF:。

LOAD DATA LOCAL INFILE 'GeoLite2-City-Blocks.csv' 
INTO TABLE geoip_blocks 
COLUMNS TERMINATED BY ',' 
OPTIONALLY ENCLOSED BY '"' 
LINES STARTING BY '::ffff:' -- import only ipv4 and cut off the ipv6 prefix
IGNORE 2 LINES 
(network_start_ip, network_mask_length, geoname_id, 
  registered_country_geoname_id, represented_country_geoname_id, postal_code, 
  latitude, longitude, is_anonymous_proxy, is_satellite_provider)
SET start_ip = inet_aton(network_start_ip),  -- set start ip as given
  end_ip = inet_aton(network_start_ip) + 
   (pow(2, (32 - (network_mask_length -96))) - 1) - 1, -- calc end_ip using mask
  network_mask_length = network_mask_length - 96 -- subtract 96 as we use ipv4

使用查询数据

SELECT geoname_id
FROM geoip_blocks
WHERE
INET_ATON('123.123.123.123') BETWEEN start_ip AND end_ip
LIMIT 1

【讨论】:

正是我想要的,谢谢!!我刚刚用 start_ip 上的主键更改了这样的选择查询,工作得非常快SELECT geoname_id FROM geoip_blocks WHERE start_ip <= INET_ATON('123.123.123.123') ORDER BY start_ip DESC LIMIT 1 @arnep,您能否在答案中添加带有数据类型和索引的 geoip_blocks 的 CREATE TABLE 脚本?谢谢!【参考方案2】:

前缀长度是网络地址中的有效位数。对于任何 IPv6 地址,这是一个介于 0-128 之间的数字,因为 IPv6 地址的长度为 128 位。例如,前缀为 128 的网络将只包含一个 IP 地址。前缀为 127 的网络将包含两个地址。 126 会有 4 个,依此类推。

如您所见,格式为 ::FFFF:10.11.12.13 的 IPv6 地址对应于去掉了 '::FFFF:' 的 IPv4 地址。 '::FFFF:' 对应于地址的前 96 位。如果 IPv6 地址的前缀长度为 126,则 IPv4 地址的前缀长度为 30(即,最后两位很重要)。

Google search 应该显示代码 sn-ps 以将 CIDR 表示法转换为范围。如果您只关心 IPv4,则最简单的方法可能是预先过滤数据,删除 ::FFFF:,从前缀长度中减去 96,并丢弃未映射到 IPv4 的地址范围。

【讨论】:

好的,所以地址范围是与network_start_ip字段的位列128到(128 - 前缀长度)的最高有效位匹配的每个可能的地址。

以上是关于在 SQL Server 中查询 Maxmind GeoLite2 City Blocks csv 以获取 IPv6 和 IPv4 地址的 geoname_id的主要内容,如果未能解决你的问题,请参考以下文章

用于sql server的免费国家,城市数据库[关闭]

将 maxmind mmbd 作为 hashmap 加载到内存中的性能差异

Rails中如何将MaxMind本地数据、CSV文件导入redis?

给定城市时查询国家/地区的 maxmind mmdb

geoip邮编查询

SQL 查询在 SQL Server CE 中很慢,但在 SQL Server 中很快