✅如果要存IP地址,用什么数据类型比较好?

典型回答

IP地址,分为IPV4和IPV6,通常IPv4地址的地址格式为nnn.nnn.nnn.nnn,如192.0.2.235,而IPv6的地址 xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx,例如2001:0db8:86a3:08d3:1319:8a2e:0370:7344

要回答好这个问题,需要区分不同的情况,因为IPV4和IPV6是不一样的。

✅什么是IPV6?和IPV4有什么区别?

先总结一下,然后再展开说。

对于IPV4和IPV6来说,字符串格式是最直观和灵活的,适合需要展示或直接操作原始地址的场景。

对于IPV4和IPV6来说,如果有些场景进行大量的位运算,或者对空间要求更加极致,那么二进制是一个好的选择


对于IPV4,如果考虑性能和空间的权衡,那么建议使用整数格式存储。因为他是一个折中方案,在效率和空间之间相对平衡。并且不至于很复杂。

而对于IPV6,如果考虑性能和空间的权衡,那么建议使用二进制格式存储。因为IPV6的整数存储方案比较复杂,需要分多个段,反而会更加复杂一些。

IPV4 存储

想要存储一个IPV4的地址,有几种办法,分别是字符串存储、32位整数存储。

字符串

将IP地址直接作为字符串存储,例如"192.168.1.1"这个ip地址,直接在数据库中就存储为varchar(15)类型,存储内容就直接是"192.168.1.1"。

字符串存储的这种方式的好处就是比较直观。但是也有缺点,那就是在进行地址比较、排序或者范围查询时可能不够高效。

IPv4地址由四个十进制数表示,每个数取值范围从0到255,中间用三个点分隔。最短的IPv4地址形式是"1.1.1.1",共7个字符;最长的形式是"255.255.255.255",共15个字符。如果每个字符占用1字节(在ASCII编码下),那么存储IPv4地址需要7到15字节的存储空间。

整数存储

IPv4地址可以采用32位的无符号整数(UNSIGNED INT)来存储。这种方法可以节省空间,并使得比较和排序操作更加高效。可以通过将IP地址的每个字节转换为整数并组合成一个单一的32位整数来实现。

将每个十进制段转换为8位的二进制数,然后合并成一个32位的整数。例如,"192.168.1.1" 转换为整数可以表示为 3232235777(在二进制下为 11000000.10101000.00000001.00000001,合并为11000000101010000000000100000001,转换为十进制后为3232235777)。

1706936223247-06f30376-85f2-4b6e-9264-d78c26e9951a.png

IPv4地址总共有32位,可以存储为一个32位的整数。无论IP地址的实际值是多少,都恒定使用4字节的存储空间。

相比于用字符串需要7-15个字节,而使用整数存储只需要4个字节,空间上是大大减少了的。


在MySQL中,对于IPv4地址,可以使用INET_ATON函数来转换IP地址为一个整数。例如:

SELECT INET_ATON('192.168.1.1');

以上SQL将返回一个整数:3232235777

二进制存储

IP地址列也可以使用BINARY(4)或VARBINARY(4)数据类型来定义,以确保每个IP地址正好使用4字节存储。

CREATE TABLE binary_ipv4_addresses (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ip_address BINARY(4)
);

将IPv4地址转换为二进制格式并插入到表中时,可以使用MySQL的INET_ATON函数将点分十进制的IP地址转换为一个整数,然后使用UNHEX函数将该整数转换为二进制形式。

假设你想插入IP地址"192.168.1.1",可以这样操作:

INSERT INTO binary_ipv4_addresses (ip_address)
VALUES (UNHEX(LPAD(HEX(INET_ATON('192.168.1.1')), 8, '0')));

查询并显示二进制格式存储的IPv4地址时,可以使用HEX函数将二进制数据转换回十六进制字符串,然后用INET_NTOA函数将其转换为人类可读的点分十进制格式:

SELECT INET_NTOA(CONV(HEX(ip_address), 16, 10)) AS ip_address_dec
FROM binary_ipv4_addresses;

这个方案的好处就是直接使用4字节存储,与整数格式类似,具有很高的空间效率。适合在底层网络操作和计算中使用,因为它与网络协议的存储方式一致。

缺点是可读性太低了,人是看不懂的。

IPV6

想要存储一个IPV6的地址,同理,也可以使用字符串存储、64位整数存储。

字符串

由于IPv6地址的长度(128位),通常将它们存储为标准的十六进制格式字符串,例如2001:0db8:85a3:0000:0000:8a2e:0370:7334。那么可以直接使用字符串存储为可读的十六进制字符串,例如 "2001:0db8:85a3:0000:0000:8a2e:0370:7334"。

同IPv4一样,字符串形式易于人类阅读。但是缺点就是需要更多的存储空间,因为每个IPv6地址最多可以包含39个字符。还有就是性能方面,字符串操作通常比数值操作要慢,特别是在排序和查询时。

二进制存储

存储IPv6的地址,还可以使用二进制数据类型(如MySQL的BINARY(16)或VARBINARY(16))存储IPv6地址的128位二进制表示。

CREATE TABLE ipv6_addresses (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ipv6_address VARBINARY(16)
);

采用这种方式,使用固定的16字节存储,比字符串格式更加紧凑。对于某些操作,如比较和索引,二进制格式可能提供更好的性能。缺点就是可读性太低了。

对于IPv6,**INET6_ATON**函数可以将IPv6地址转换为一个二进制字符串,这个字符串可以存储在一个BLOB列中。尽管这不是直接转换为一个整数,但它提供了一种方式来存储IPv6地址。将IPv6地址转换为二进制表示的例子:

SELECT INET6_ATON('2001:0db8:85a3:0000:0000:8a2e:0370:7334');

当想要插入一个IPV6地址时:

INSERT INTO ipv6_addresses (ipv6_address)
VALUES (INET6_ATON('2001:0db8:85a3:0000:0000:8a2e:0370:7334'));

当需要读取存储的IPv6地址并将其转换回可读的文本格式时,可以使用**INET6_NTOA**函数:

SELECT id, INET6_NTOA(ipv6_address) AS ipv6_text
FROM ipv6_addresses;

整数存储

如果用整数存储的话,可以将IPv6地址分成多个部分(如两个BIGINT或者4个INT)。

例如, "2001:0db8:85a3:0000:0000:8a2e:0370:7334",可以拆分成"2001:0db8:85a3:0000" 和 "0000:8a2e:0370:7334" ,在不分别转换为整数后,可以存储为两个整数值(需要将十六进制转换为十进制表示)。

这种方式的优点就是操作灵活性比较高,允许直接对IPv6地址的特定部分进行操作和查询。还有就是数值比较通常比字符串快,有利于性能。

缺点就是需要额外的逻辑来合并和分割这些数值以重构完整的IPv6地址。

原文: https://www.yuque.com/hollis666/xkm7k3/py1ytrmw65n0b0qt