我正在解析两个包含IP地址的CSV文件。 第一个是源CSV,第二个是“黑名单”。
由于源文件的大小,我正在尝试优化找到与黑名单匹配的IP地址的速度。
编辑:黑名单由IP地址“块”组成。这意味着黑名单中的每条记录都有两个IP地址:一个Start
块(例如216.254.128.0)和一个End
块。 (例如,216.254.223.255)
这意味着直接查找等不起作用。
我想知道解决这个问题的最佳方法是什么。蛮力方法是:
String[] parts = sourceIP.split("\\."); // String array, each element is text between dots
int hi = 255;
int lo = 0;
int mid = (hi - lo) / 2 ;
if (Integer.valueOf(parts[0]) > mid) {
mid = lo;
}
然后,我可以为每个part
重复此操作,以确定IP地址是否在黑名单中。
这看起来相当激进,有4k +记录,这可能需要非常长的时间。
可能需要10多次迭代来决定每个部分,然后必须重复这些迭代以检查黑名单中IP块的“高”部分。这是每条记录80多次迭代。
我希望在这里获得一些输入,以查看比较IP地址的最佳方法。
你有什么想法?
是否可以使用快速按位掩码通过序列化INetAddress
来快速比较值?
文件结构澄清:
源IP文件:
包含数据库中的记录列表。 (Aprox 4k)。每条记录都包含姓名,地址,电子邮件和IP地址。
黑名单:
包含4.2k记录。每条记录都是一个IP地址“块”。这包括两个IP地址。 1.开始和2.结束。
如果源列表中的记录具有在黑名单中找到的IP地址,我需要保存该记录并将其添加到新文件中。
答案 0 :(得分:4)
我假设您正在谈论xxx.xxx.xxx.xxx格式的IPV4地址。
您可以轻松地将IP地址转换为整数。每个段(即xxx)是8位(即一个字节)。因此,它们中的四个一起构成一个32位整数。因此,给定类似" 192.168.100.12"的IP地址,您可以将其拆分为四个部分,将每个部分解析为一个字节并创建一个整数。例如,假设您创建了段的字节数组:
ipBytes[0] = 192;
ipBytes[1] = 168;
ipBytes[2] = 100;
ipBytes[3] = 12;
您可以将其转换为整数:
int ipAddress = ipBytes[0];
ipAddress = (ipAddress << 8) | ipBytes[1];
ipAddress = (ipAddress << 8) | ipBytes[2];
ipAddress = (ipAddress << 8) | ipBytes[3];
有更有效的方法可以做到这一点,但你明白了。您的语言的运行时库可能已经包含了解析IP地址并为您提供字节以使其成为整数的内容。
您有一组要检查源地址的IP地址范围。将每个范围加载到如下结构中:
class IPRange
{
public int startIp;
public int stopIp;
}
并将它们存储在数组或列表中。然后通过启动IP地址对列表进行排序。
对于每个源IP地址,将其转换为整数并对列表进行二进制搜索,搜索起始IP地址。源地址本身可能不会(可能不会被找到),但是当二进制搜索终止时,mid
值将保留其起始IP地址小于或等于源的范围的索引地址。然后,您只需根据该项目的结束IP地址检查源地址,看它是否在该范围内。
二进制搜索是O(log n)。如果您正在搜索4,300个范围的列表,那么它最多需要13个探测器来查找阵列中的地址。即使在进行4,000次不同的搜索时,这应该足够快。您只是在讨论范围数组的50,000个探测器的顺序。
几点说明:
首先,正如我上面所说,我假设你在谈论IPV4地址。如果您正在谈论IPV6地址,相同的概念仍然适用,但您需要64位整数。我对IPv6知之甚少,不知道如何将地址转换为64位整数。可能你应该依赖你的运行时库来获取地址字节。
第二:我认为范围不重叠。也就是说,你不会有类似的东西:
start range end range
192.168.1.1 192.168.2.255
192.168.2.1 192.168.3.255
如果您有,那么IP地址可能属于这两个范围。您可能构建重叠范围,以允许地址通过裂缝。如果你有重叠范围,问题会变得有点复杂。
答案 1 :(得分:0)
将两个文件放在String中。使用split(&#34;,&#34;)拆分第一个字符串中的ip。循环通过获得的ips数组。对于每个ip在第二个字符串中搜索它,如blacklist.indexOf("," + ip + ",")
但首先添加&#34;,&#34;在黑名单字符串的开头和结尾。
答案 2 :(得分:0)
蛮力。 将所有内容加载到ram中,没有理由不这样做。 将ips拆分为2d阵列。 {0:123123123123} 黑名单到3d数组。 现在你可以开始搜索整数了。 当你有匹配时比较下一节。 如果源值较高,则与END块相同的部分进行比较。 当您将匹配推送到新阵列并将其写入最后的文件时。 如果这需要更多的时间来运行,那么我就输入了这个,然后关闭你打开的色情片,因为你的ram已满并使用你的页面文件。
答案 3 :(得分:0)
您可以使用名为Bloom Filter的数据结构。这是相当有效的性能和存储方式。至于一个例子,这里有一个问题,Most Efficient way of implementing a BlackList有一个建议的答案。
据我所知,谷歌Chrome也使用了这种技术,并且在 Matthials Vallentine的博客文章A Garden Variety of Bloom Filters中也得到了很好的解释。
在Adobe leaked credentials checker可以找到更简洁的解释。一些摘录
原始泄漏约为9.3GB未压缩,其中3.3GB是电子邮件 地址[...]这意味着数据可以容纳512MB(即232位) 内存并允许我们在恒定时间内执行查找[...] An 允许占用840MB的最佳布隆过滤器 几乎没有任何误报。
答案 4 :(得分:0)
似乎最方向的解决方案是使用interval tree来存储黑名单。然后检查IP是否与任何间隔相交。
您还可以考虑使用Trie / hashtable来获取间隔相同的快速查找。 IE:216.254.128.0到216.254.223.255可以合并到216.254。(128.0,223.255),其中()是间隔。因此,您最终会得到两个哈希表查找(一个用于216,一个用于254),然后在区间树中搜索,这可能只包含少量元素。
您还可以将重叠间隔合并到一个间隔中,这可以在构建间隔树时完成。在这种情况下,最终更像是二叉搜索树。