在mongoDB中保存IP地址

时间:2013-03-23 19:39:32

标签: mongodb ip-address

目前,为了保存IP地址,我将其转换为数字并将其存储在集合中。基本上我这样做是为了记录日志。这意味着我尽可能快地存储信息并且空间最小。

我很少用它来查询。

我的想法

  • 存储为字符串肯定是低效的。
  • 存储为4位数将会变慢并占用更多空间。

尽管如此,我认为这是一个适当的方法,但是出于我的目的还有更好的方法吗?

4 个答案:

答案 0 :(得分:11)

如果您不介意花费额外的工作量,请务必将IP地址保存为数字,特别是如果您需要对地址进行查询并且您拥有大型表/集合。

原因如下:

<强>存储

  • 如果存储为无符号整数,则IPv4地址为4个字节。
  • 当以点状形式写成字符串时,IPv4地址在10字节和18字节之间变化。 (假设平均值是14个字节。)

对于字符,这是7-15个字节,如果您使用的是可变长度字符串类型,则会增加2-3个字节,这取决于您正在使用的数据库。如果您有可用的固定长度字符串表示,则必须使用15个字符的固定宽度字段。

磁盘存储很便宜,因此在大多数用例中不是一个因素。但是,内存并不便宜,如果你有一个大型表/集合并且想要快速查询,那么你需要一个索引。字符串编码的2-3x存储惩罚大大减少了您可以索引的记录数量,同时仍然保持索引驻留在内存中。

  • 如果存储为无符号整数,则IPv6地址为16字节。 (可能是多个4或8字节整数,具体取决于您的平台。)
  • 当使用缩写的十六进制表示法编码为字符串时,IPv6地址的范围为6个字节到42个字节。

在低端,回送地址(:: 1)是3个字节加上可变长度字符串开销。在高端,像2002:4559:1FE2:1FE2:4559:1FE2:4559:1FE2这样的地址使用39个字节加上可变长度字符串开销。

与IPv4不同,假设平均IPv6字符串长度为6和42的平均值是不安全的,因为具有大量连续零的地址数量只占整个IPv6地址空间的一小部分。只有一些特殊的地址,比如loopback和autoconf地址,才有可能以这种方式压缩。

同样,对于字符串编码与整数编码,这是一个> 2x的存储损失。

网络数学

您认为路由器将IP地址存储为字符串吗?当然他们没有。

如果您需要对IP地址进行网络数学运算,则字符串表示很麻烦。例如。如果要编写查询特定子网上的所有地址的查询(“返回IP地址在10.7.200.104/27中的所有记录”,则可以通过使用整数子网掩码屏蔽整数地址来轻松完成此操作。 Mongo不支持这个特定的查询,但是大多数RDBMS都支持。)如果你将地址存储为字符串,那么你的查询需要将每一行转换为一个整数,然后屏蔽它,这要慢几个数量级。(按位屏蔽)对于IPv4地址,可以使用2个寄存器在几个CPU周期内完成。将字符串转换为整数需要在字符串上循环。)

类似地,使用整数地址的范围查询(“返回所有记录在192.168.1.50和192.168.50.100之间的所有记录”)将能够使用索引,而字符串地址上的范围查询则不会。

底线

它需要更多的工作,但不多(有一百万aton()和ntoa()函数在那里),但如果你正在构建一些严肃而坚实的东西,你想要面向未来未来的要求和大型数据集的可能性,您应该将IP地址存储为整数,而不是字符串。

如果您正在做一些快速而肮脏的事情,并且不介意将来重塑的可能性,那么请使用字符串。

对于OP的目的,如果您正在优化速度和空间并且您认为不想经常查询它,那么为什么要使用数据库呢?只需将IP地址打印到文件即可。这比将其存储在数据库中具有更快,更高的存储效率(具有相关的API和存储开销)。

答案 1 :(得分:1)

将ip地址另存为int的有效方法。如果你想用cidr过滤器标记一个ip,那么这里有一个演示:

> db.getCollection('iptag').insert({tags: ['office'], hostmin: 2886991873, hostmax: 2887057406, cidr: '172.20.0.0/16'})
> db.getCollection('iptag').insert({tags: ['server'], hostmin: 173867009, hostmax: 173932542, cidr: '10.93.0.0/16'})
> db.getCollection('iptag').insert({tags: ['server'], hostmin: 173932545, hostmax: 173998078, cidr: '10.94.0.0/16'})

创建标签索引。

> db.getCollection('iptag').ensureIndex(tags: 1)

使用cidr范围过滤ip。 ip2int('10.94.25.32') == 173938976

> db.getCollection('iptag').find({hostmin: {$lte: 173938976}, hostmax: {$gte: 173938976}})

答案 2 :(得分:0)

IPv4是四个字节,因此您可以将其存储为32位整数(BSON类型16)。

请参阅http://docs.mongodb.org/manual/reference/bson-types

答案 3 :(得分:-1)

IPv4最简单的方法是使用提供的有趣数学here转换为int。

在与db

匹配之前,我使用以下函数(js)进行转换
ipv4Number: function (ip) {
    iparray = ip.split(".");
    ipnumber = parseInt(iparray[3]) +
        parseInt(iparray[2]) * 256 +
        parseInt(iparray[1]) * Math.pow(256, 2) +
        parseInt(iparray[0]) * Math.pow(256, 3);
    if (parseInt(ipnumber) > 0)return ipnumber;
    return 0;
}