目前,为了保存IP地址,我将其转换为数字并将其存储在集合中。基本上我这样做是为了记录日志。这意味着我尽可能快地存储信息并且空间最小。
我很少用它来查询。
我的想法
尽管如此,我认为这是一个适当的方法,但是出于我的目的还有更好的方法吗?
答案 0 :(得分:11)
如果您不介意花费额外的工作量,请务必将IP地址保存为数字,特别是如果您需要对地址进行查询并且您拥有大型表/集合。
原因如下:
<强>存储强>
对于字符,这是7-15个字节,如果您使用的是可变长度字符串类型,则会增加2-3个字节,这取决于您正在使用的数据库。如果您有可用的固定长度字符串表示,则必须使用15个字符的固定宽度字段。
磁盘存储很便宜,因此在大多数用例中不是一个因素。但是,内存并不便宜,如果你有一个大型表/集合并且想要快速查询,那么你需要一个索引。字符串编码的2-3x存储惩罚大大减少了您可以索引的记录数量,同时仍然保持索引驻留在内存中。
在低端,回送地址(:: 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)。
答案 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;
}