14

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

我很少用它来查询。

我的想法

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

尽管如此,我认为这是一种适当的方法,但有没有更好的方法适合我的目的?

4

4 回答 4

14

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

原因如下:

贮存

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

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

磁盘存储很便宜,因此在大多数用例中这不是一个因素。然而,内存并不便宜,如果您有一个大表/集合并且想要进行快速查询,那么您需要一个索引。字符串编码的 2-3 倍存储损失极大地减少了您可以索引的记录数量,同时仍将索引保留在内存中。

  • 如果存储为无符号整数,则 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 地址,可能会以这种方式进行压缩。

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

网络数学

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

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

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

底线

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

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

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

于 2013-12-30T18:59:04.813 回答
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}})
于 2017-06-30T01:03:20.163 回答
0

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

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

于 2013-11-15T08:38:18.857 回答
-1

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

我使用以下函数(js)在与db匹配之前进行转换

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;
}
于 2014-04-13T05:06:53.230 回答