在内存中存储大量IP地址

时间:2012-02-09 00:30:25

标签: java data-structures out-of-memory sparse-matrix bitset

问题有点长,请耐心等待。

我正在编写java代码,将来自全天网络跟踪的流量汇总到每个子网的84秒箱。目前,每个子网最多有256个子网和1024个bin。我使用它来获取流量特征统计信息,例如连接数,输入/输出字节,每个子网的每个窗口中的外部IP地址数。虽然连接,输入/输出字节很简单,但获取唯一数量的外部IP地址会导致OutOfMemory错误。

要确定外部IP地址的唯一数量,我需要在某些数据结构(如哈希表)中存储IP地址,并且在跟踪结束时,我可以获得此哈希表的大小。这意味着我将拥有1024 * 256个哈希表,每个哈希表存储大量12-15字节的IP地址字符串(数十到数千)。这很快就会爆炸,系统内存不足(我试图将java堆大小设置为高达2GB而无济于事)。有人能建议一种有效存储大量物体的方法吗?

我尝试使用bitset(将ip转换为int)但是考虑到ip地址非常稀疏,它对内存情况没有帮助。作为最后的手段,我可​​能会使用colt库稀疏矩阵,每个double存储多达64个ip地址,但我想得到一个意见,以防我遗漏了一些明显的东西,并且可以节省编写/调试这样的包装器。

旁注:为了了解规模,我看到每个跟踪有几亿个流,我会解析并聚合。在大多数情况下,我使用的是256个子网中的10到20个,但我希望该解决方案可以扩展到所有256个子网。

3 个答案:

答案 0 :(得分:1)

不确定为什么你有1024 * 256?

您只需要一个数据结构来保存所有数据;使用由IP键入的红黑树作为4字节整数。这给你O(log(n))查找时间,这意味着最坏的情况是找到IP的32次比较。或者由HashMap键入的Integer

在每个节点中都有84个“bin”对象(存储在链接列表,数组或任何有关访问模式的内容中),其中包含您要存储的信息。如果您只需要聚合...只存储聚合。这真的会减少你的内存使用量。

编辑:我倾向于忘记正在签名的Java int。这不会造成问题,除非您真的想要轻松地对它们进行排序,在这种情况下使用long / Long

答案 1 :(得分:1)

<强>更新 如果您将整个4亿个IPv4地址存储为单个阵列,那么您可以将时间表示为单个短路。

short[] ipv4 = new short[Integer.MAX_VALUE * 2]; // technically not possible blah blah

这是8GB,65K时间分辨率。只要考虑一下,因为它在内存上设置了上限,因为任何其他方案都必须在其下面。如果您使用了一个字节,它将是256个时间分辨率,每个bin为337.5秒,并且为4 GB。

现在,您只能说您在该存储桶中至少看到了一个数据包。如果你需要一个可以再次耗尽内存的计数,但是如果需要短暂的话,可以使用1024个桶,其潜在的6位分辨率用于计数:最大64个数据包。

现在拥有1亿个独特的IP,可将内存减少10倍,因此理论上从8GB到800MB。虽然没有分配整个空间,但您认为可以节省内存,但是您仍然需要为每个IP存储4个字节:400MB仅用于IP + 400MB用于某种排序结构来保存它们(100M指针* 4个字节),以及2个字节用于时间:最低1GB。通过分配完整空间,您可以跳过再次存储IP,因为您的哈希是您的IP。如果减少阵列,则不再使用IP,因为它已经被删除了。现在你无法存储IP并且仍然可以回答IP给出的问题,但你不能反复使用它。

如果您存储了一系列子网掩码,然后汇总了其下的所有IP,并将您的统计信息保存在该子网掩码上,该怎么办?例如,您有256个具有自己的子网掩码的子网。你的程序会占用掩码的下限。如果你屏蔽是209.134.0.0/16并使用8的下限。那么它将为该子网创建256个二进制文件,它们是209.134.0.0-209.134.255.255的一部分。您将为所有256个子网重复相同的过程。使用8位的下限意味着将汇总每个子网的较低256个地址。您可以将任何IP地址散列到bin中并将统计信息保存在内存中。但是,您无法说出任何有关单个IP地址的信息。但是,如果您需要更高的分辨率,您可以将较低的子网掩码放到4,现在有更多的分档。

如果你有1个IP,你只能创建一个bin,所以如果你没有IP显示那里你可以节省一些空间,所以它的平衡行为在足够低的下降分辨率之间,但足够高以跳过创建bin的你不需要的东西。

然后你可以写出每个bin的日志并跟踪磁盘上每个bin中发生的事情。如果你想回答关于单个IP的问题,你可以找出它所属的bin,然后打开文件并在那里搜索以找到答案。此方案意味着您可以根据数据的大小向上或向下扩展,也可以通过提高和降低边界来扩展或缩小。通过更改每个bin写出的文件结构,可以提高性能。

我知道对不起! : - )

答案 2 :(得分:0)

我会有多个BitSet,例如

private final BitSet[] ips = new BitSet[256*256*256];

public void sample(int address) {
   BitSet bs = ips[address >>> 8];
   if (bs == null)
      ips[address >>> 8] = new BitSet();
   bs.set(address & 0xFFFF);
}

public int count() {
   int total = 0;
   for(BitSet bs: ips)
      total += bs.cardinality();
   return total;
}

每个地址只有1位,具体取决于IP地址的备用空间。鉴于不会出现许多地址范围,内存消耗可能非常有效。没有真实的数据集很难测试。 ;)

最坏情况下的内存大小为512 MB,但对于实际数据集,它应该远小于此值。