如何及时搜索大型IP地址数据库?

时间:2015-04-28 14:59:02

标签: python-2.7 csv

我有一个像这样读取的CSV文件:

with open(r"file.csv", 'rb') as f:
    reader = csv.reader(f)
    c = list(reader)

此操作产生了大约22000个其他列表的1个列表。格式为:

[['10.0.0.0/24', 'random bla', 'vlan=22'], ['20.0.0.0/20', 'random bla 2', vlan=354] ...x22000]

这是一个只包含网络,vlan,描述等的IP数据库。我制作了一个脚本来检查数据库中是否存在任意输入。对于我需要在数据库中检查的每个网络,我需要执行以下操作:

  • 在IPNetwork对象中转换我的字符串(我正在使用netaddr模块)。
  • 对于CSV转储中的每个列表,使用IPNetwork(CSV_inside_list [0])转换IP对象中主列表内列表的第一个元素。
  • 如果我的字符串位于CSV网络中,请打印整个列表(CSV_inside_list)。
  • 执行此操作[要比较的IP数量] * [数据库大小,当前为22K] =超时消耗。

我对你的要求是:我怎样才能加快速度?我不能简单地做“if my_ip in csv_database”,因为我需要它们是IPNetwork对象,所以例如10.0.0.1匹配,如果面对10.0.0.0/24,因为该IP在网络范围内。

1 个答案:

答案 0 :(得分:2)

目前,您在检查IP之前将所有网络加载到内存中,但实际上并不需要这样做,您只需迭代读取器而不是将其转换为列表。

相反,我会加载所有IP地址以检查列表并对它们进行排序。然后可以使用bisect以对数时间从列表中获取单个网络中的所有IP,因此不是O(m*n)而是O(m*log(n)),而是排序成本地址列表。

应该与此类似*:

from bisect import bisect_left, bisect_right

def find_ips_in_network(network, sorted_ips):
    first = netaddr.IPAddress(network.first)
    last = netaddr.IPAddress(network.last)
    first_idx = bisect_left(sorted_ips, first)
    last_idx = bisect_right(sorted_ips, last)
    return sorted_ips[first_idx:last_idx]

sorted_ips = sorted(...)  # load ips as sorted list of netaddr.IPAddress
found_networks = dict()
# or collections.defaultdict(list) if you want all networks

with open(r"file.csv", 'rb') as f:
    reader = csv.reader(f)
    for row in reader:
        network = netaddr.IPNetwork(row[0])
        for ip in find_ips_in_network(network, sorted_ips):
            found_networks[ip] = row
            # or found_networks[ip].append(row) if using defaultdict

for ip in sorted_ips:
    if ip in found_networks:
        print ip, "found in:", found_networks[ip]

*免责声明:无法保证正确性

编辑:小优化:由于首先计算第一个地址的索引,因此可以在搜索最后一个索引时限制下限:

    last_idx = bisect_right(sorted_ips, last, first_index)

说明: bisect_leftbisect_right使用binary search搜索已排序列表中的给定值,并返回值必须具有的索引插入到主菜单列表的排序。 bisect_leftbisect_right之间的区别在于值已经在列表中(一次或多次),bisect_left将返回第一个匹配的索引{{1最后一个+ 1的索引 所以在这种情况下:

bisect_right

将返回first_idx = bisect_left(sorted_ips, first) 的第一个IP地址的索引,该索引大于或等于sorted_ips,并且

first

在最后一个IPAddress小于或等于last_idx = bisect_right(sorted_ips, last, first_idx) 之后返回一个索引。 我们知道lastfirst <= last必须为fist_idx,因此第二次搜索的下限可以限制为<= last_idx

这意味着切片first_idx包含sorted_ips[first_idx:last_idx]列表中的所有IP。如果列表中的地址不相等或者在first <= ip <= lastfirst之间,则两个索引都相同,返回一个空列表。

由于二进制搜索的最差情况是last,因此查找列表中的哪些IP在网络中然后检查所有IP的所有网络,特别是如果要检查的IP列表,这将快得多非常大。