Algorithm 批量地理定位数百万IP

Algorithm 批量地理定位数百万IP,algorithm,postgresql,geolocation,mapping,ip,Algorithm,Postgresql,Geolocation,Mapping,Ip,我有200万个IP地址和2500万个IP范围,其中起始IP、结束IP和地理位置存储在PostgreSQL中。有没有一种有效的方法从2500万数据库中查找这200万IP的地理位置?我所做的是比较IP地址是否位于起始IP和结束IP之间,并查找相应的位置。然而,这似乎要花很长时间。这可能更像是从一组范围中查找一组整数,例如从以下位置搜索{7,13,31,42}: Start End Loc 1 10 US 11 20 US 21 26 CN 29 32 SE 33

我有200万个IP地址和2500万个IP范围,其中起始IP、结束IP和地理位置存储在PostgreSQL中。有没有一种有效的方法从2500万数据库中查找这200万IP的地理位置?我所做的是比较IP地址是否位于起始IP和结束IP之间,并查找相应的位置。然而,这似乎要花很长时间。这可能更像是从一组范围中查找一组整数,例如从以下位置搜索{7,13,31,42}:

Start End Loc
1     10  US
11    20  US
21    26  CN
29    32  SE
33    45  CA
返回:

7  US
13 US
31 SE
42 CA
请注意,范围可能不一定连接,大小也可能不相同。谢谢大家!

编辑

作为一个具体的例子,以下是我正在处理的数据:

     start_ip     |      end_ip      | country |  region   |   city    | 
------------------+------------------+---------+-----------+-----------+-
 1.33.254.73/32   | 1.33.254.73/32   | jpn     | 33        | kurashiki | 
 1.39.1.0/32      | 1.39.4.255/32    | ind     | mh        | mumbai    | 
 1.40.144.0/32    | 1.40.145.255/32  | aus     | ns        | fairfield | 
 1.40.235.0/32    | 1.40.242.255/32  | aus     | ns        | sydney    | 
 1.44.28.0/32     | 1.44.29.255/32   | aus     | vi        | melbourne | 
 1.44.82.0/32     | 1.44.83.255/32   | aus     | vi        | melbourne | 
 1.44.92.0/32     | 1.44.93.255/32   | aus     | vi        | melbourne | 
 1.44.128.0/32    | 1.44.129.255/32  | aus     | vi        | melbourne | 
 1.44.220.0/32    | 1.44.221.255/32  | aus     | vi        | melbourne | 
 ......
 ......
这些查询类似于:

 75.149.219.61/32
 68.239.61.29/32
 96.41.50.165/32
 183.62.126.7/32
 ......
如果您要查询Loc列,则应将其添加到该列。此外,由于这是一个3列的表,因此最好将StartIP和EndIP组合起来,将其用作键,并将Geolocation用作值,然后从键值存储(如或)中读取所有内容。NoSQL/无表数据存储就是为这类事情而设计的,在这种情况下,您可以读取数以百万计的数据点

编辑:在阅读了一些评论之后,我想到另一个解决方案是通过MapReduce之类的东西来并行化搜索。分配线程以查询IP范围,例如Thread1:1-10、Thread2:11-20等。。。在映射步骤中,然后在reduce步骤中分配线程以将分段查询缩减为一个结果。显然,您需要一种单独的编程语言来编写此脚本,但并发性将有助于减少总体加载时间,尽管缺点是对数据库进行多次查询。

如果您要查询Loc列,则应将其删除。此外,由于这是一个3列的表,因此最好将StartIP和EndIP组合起来,将其用作键,并将Geolocation用作值,然后从键值存储(如或)中读取所有内容。NoSQL/无表数据存储就是为这类事情而设计的,在这种情况下,您可以读取数以百万计的数据点


编辑:在阅读了一些评论之后,我想到另一个解决方案是通过MapReduce之类的东西来并行化搜索。分配线程以查询IP范围,例如Thread1:1-10、Thread2:11-20等。。。在映射步骤中,然后在reduce步骤中分配线程以将分段查询缩减为一个结果。显然,您需要一种单独的编程语言来编写脚本,但并发性将有助于减少总体加载时间,尽管缺点是对数据库进行多次查询。

我认为,最好、更优雅的解决方案是存储IP和范围 作为inet格式。IP范围通常以网络/掩码格式发布, 不是作为开始/结束。这允许编写基于连接的

ON (ip.addr << geoloc.range)
实际上是四个独立的连续范围的合并:

11101011   235.0-235.255
    11101100   236.0-239.255
    11101111   
    11110000   240.0-241.255   
    11110001
11110010   242.0-242.255
因此,将行分解为CIDR操作所需的四行是不实际的

cidr数据类型中的开始/结束外观,因此将它们转换为inet它们都是/32。。。将查询的值也保留在inet数据类型中,在开始和结束时进行索引,应该会给出合理的结果:

 SELECT query.ip, geoloc.country, geoloc.region, geoloc.city
     FROM query JOIN geoloc
     ON (query.ip >= geoloc.start_ip AND query.ip <= geoloc.end_ip);
另一种选择,实际上不是很优雅,是基于addr和range的第一个字节,将ip和geoloc表分解为单独的子表。我不希望您的ip范围具有不同的第一个字节

 SELECT * FROM geoloc
     WHERE start_ip >= inet '5.0.0.0' and end_ip <= inet '5.255.255.255'
     INTO TABLE geoloc_5;

 SELECT * FROM query
     WHERE start_ip >= inet '5.0.0.0' and end_ip <= inet '5.255.255.255'
     INTO TABLE query_5;

 Remember to CREATE INDEX on geoloc_5 start_ip, end_ip

这一方法在几年前确实适用于一个大型PostgreSQL批处理,但我预计,从那时起,一个更聪明的索引管理器——加上专用的数据类型——将发展到与这种DIY分区相匹配的程度。因此,如果最好、更优雅的解决方案是存储IP和范围,那么naive Jordan分区应该只作为最后一个解决方案使用 作为inet格式。IP范围通常以网络/掩码格式发布, 不是作为开始/结束。这允许编写基于连接的

ON (ip.addr << geoloc.range)
实际上是四个独立的连续范围的合并:

11101011   235.0-235.255
    11101100   236.0-239.255
    11101111   
    11110000   240.0-241.255   
    11110001
11110010   242.0-242.255
因此,将行分解为CIDR操作所需的四行是不实际的

cidr数据类型中的开始/结束外观,因此将它们转换为inet它们都是/32。。。将查询的值也保留在inet数据类型中,在开始和结束时进行索引,应该会给出合理的结果:

 SELECT query.ip, geoloc.country, geoloc.region, geoloc.city
     FROM query JOIN geoloc
     ON (query.ip >= geoloc.start_ip AND query.ip <= geoloc.end_ip);
另一种选择,实际上不是很优雅,是基于addr和range的第一个字节,将ip和geoloc表分解为单独的子表。我不希望您的ip范围具有不同的第一个字节

 SELECT * FROM geoloc
     WHERE start_ip >= inet '5.0.0.0' and end_ip <= inet '5.255.255.255'
     INTO TABLE geoloc_5;

 SELECT * FROM query
     WHERE start_ip >= inet '5.0.0.0' and end_ip <= inet '5.255.255.255'
     INTO TABLE query_5;

 Remember to CREATE INDEX on geoloc_5 start_ip, end_ip
这一方法在几年前确实适用于一个大型PostgreSQL批处理,但我预计,从那时起,一个更聪明的索引管理器——加上专用的数据类型——将发展到与这种DIY分区相匹配的程度。因此,只有在您必须支持的情况下,NaiveJordan分区才应该作为最后一个解决方案使用 请参阅您的查询和查询计划,以获取对此的有意义的输入。例如:

explain select hits.ip, locations.loc
 from hits left outer join locations
   on (hits.ip >= locations.start and hits.ip <= locations.stop);
                                  QUERY PLAN                                   
-------------------------------------------------------------------------------
 Nested Loop Left Join  (cost=0.00..245.06 rows=2400 width=36)
   Join Filter: ((hits.ip >= locations.start) AND (hits.ip <= locations.stop))
   ->  Seq Scan on hits  (cost=0.00..34.00 rows=2400 width=4)
   ->  Materialize  (cost=0.00..1.07 rows=5 width=40)
         ->  Seq Scan on locations  (cost=0.00..1.05 rows=5 width=40)
(5 rows)
我不确定您是否希望像其他答案所建议的那样将位置数据添加到索引中。这只是死数据添加膨胀,不利于查找行


即使您使用的pg版本支持仅索引扫描9.2(仍处于测试阶段),更小更精简的索引可能仍会提供更快的结果,每行增加一个元组查找。

您必须提供查询和查询计划,以便在此基础上进行有意义的输入。例如:

explain select hits.ip, locations.loc
 from hits left outer join locations
   on (hits.ip >= locations.start and hits.ip <= locations.stop);
                                  QUERY PLAN                                   
-------------------------------------------------------------------------------
 Nested Loop Left Join  (cost=0.00..245.06 rows=2400 width=36)
   Join Filter: ((hits.ip >= locations.start) AND (hits.ip <= locations.stop))
   ->  Seq Scan on hits  (cost=0.00..34.00 rows=2400 width=4)
   ->  Materialize  (cost=0.00..1.07 rows=5 width=40)
         ->  Seq Scan on locations  (cost=0.00..1.05 rows=5 width=40)
(5 rows)
我不确定您是否希望像其他答案所建议的那样将位置数据添加到索引中。这只是死数据添加膨胀,不利于查找行


即使您使用的pg版本支持仅索引扫描9.2(仍处于测试版),更小的精简索引可能仍会提供更快的结果,每行增加一个元组查找。

IP是以inet格式存储的,还是以其他方式存储的?如果开始、结束和IP实际存储为整数,我建议将开始和结束分解为单独的行,如图所示:,对结果进行排序,并在其中执行查找,包括您当前获得的查询计划及其附带的查询。IP是否以inet格式存储,或者以其他方式存储?如果开始、结束和IP实际存储为整数,我建议将开始和结束分解为单独的行,如图所示:,对结果进行排序,并在其中进行查找,包括您当前获得的查询计划和相应的查询。我不同意要获得良好的响应时间,您必须跳出关系数据库,进入NoSQL解决方案。无论系统如何,返回200万行都是一个非常大的结果集。然后,他实际上需要将StartIP EndIP分解为一个值范围,否则我看不出当实际存储的密钥为192.168.1.0-192.168.1.255时,密钥库会如何找到192.168.1.17。爆炸geoloc表。。。看起来有点像试图将Internet存储在密钥库上:-我不同意要获得良好的响应时间,必须从关系数据库跳到NoSQL解决方案上。无论系统如何,返回200万行都是一个非常大的结果集。然后,他实际上需要将StartIP EndIP分解为一个值范围,否则我看不出当实际存储的密钥为192.168.1.0-192.168.1.255时,密钥库会如何找到192.168.1.17。爆炸geoloc表。。。看起来有点像试图将Internet存储在密钥库上:-我的示例查询可能不会产生正确的结果,除非将其缩小到只包含最近的范围。假设a.0.0.0/8->USA,而a.b.0.0/16->TX。您只需要a.b.1.14的TX。我的示例查询可能不会产生正确的结果,除非您将其缩小到仅包括最近的范围。假设a.0.0.0/8->USA,而a.b.0.0/16->TX。您只需要a.b.1.14的TX。