背景
笔者在一次面试中被面道此问题,当时想都没想肯定是字符串啊。当被问到使用字符串那长度你准备设计成多长呢?我又回答:“通常情况下,在保存ipv4地址时,一个ipv4最小也需要7个字符串,最大需要15个字符串,所以,使用varchar(15)即可。”面试官的表情似乎满意又不是很满意的感觉,于是他和我说:“字符串可以存,但是会占用存储空间并且查询效率不高。”后来我才想起来,MySQL在保存变长的字符串时,还需要额外的一个字节来保存此字符串的长度,而如果使用无符号整数来存储,只需要4个字节即可。
使用无符号整数
优缺点
相对于字符串存储,使用无符号整数存储有如下好处:
- 节省空间,不管是数据存储空间,还是索引空间
- 便于使用范围(between…and)查询,而且效率更高
使用无符号整数来存储也有缺点:
- 不便于阅读
- 需要手动转换
如何转换
IPv4
介绍
网际协议版本4,又称互联网通信协议第四版,是网际协议开发过程中的第四个修订版本,也是此协议第一个被广泛部署的版本。IPv4是互联网的核心,也是使用最广泛的网际协议版本。
IPv4是一种无连接的协议,操作在使用分组交换的链路层(如以太网)上。此协议会尽最大努力交付数据包,意即它不保证任何数据包均能送达目的地,也不保证所有数据包均按照正确的顺序无重复地到达。这些方面是由上层的传输协议(如传输控制协议)处理的。
到2019年底,全球所有43亿个IPv4地址已分配完毕,这意味着没有更多的IPv4地址可以使用。
| 格式 | 值 | 从点分十进制转换 |
|---|---|---|
| 点分十进制 | 192.0.2.235 | 不适用 |
| 点分十六进制 | 0xC0.0x00.0x02.0xEB | 每个字节被单独转换为十六进制 |
| 点分八进制 | 0300.0000.0002.0353 | 每个字节被单独转换为八进制 |
| 十六进制 | 0xC00002EB | 将点分十六进制连在一起 |
| 十进制 | 3221226219 | 用十进制写出的32位整数 |
| 八进制 | 030000001353 | 用八进制写出的32位整数 |
建议:当存储ipv4地址时,应该使用32位无符号整数(UNSIGNED INT)存储ip,而不是使用[字符串]
- INET_ATON: 把字符串格式的ip转换成整数
- INET_NTOA: 把整数格式的ip转换成字符串 具体操作如下所示:
mysql> select inet_aton('127.0.0.1');
+------------------------+
| inet_aton('127.0.0.1') |
+------------------------+
| 2130706433 |
+------------------------+
1 row in set (0.00 sec)
mysql> select inet_ntoa(2130706433);
+-----------------------+
| inet_ntoa(2130706433) |
+-----------------------+
| 127.0.0.1 |
+-----------------------+
1 row in set (0.00 sec)
IPv6
介绍
IPv6即互联网协议第6版,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址。IPv6的地址长度为128位,是IPv4地址长度的4倍。于是IPv4点分十进制格式不再适用,采用十六进制表示。
建议:当存储ipv6地址时,应该使用128位无符号整数(VARBINARY)存储ip,而不是使用[字符串]
- INET6_ATON: 把字符串格式的ip转换成整数
- INET6_NTOA: 把整数格式的ip转换成字符串
具体操作如下所示:
该函数把IPv6字符串地址转换为无符号整型二进制数据:
mysql> select inet6_aton('ABCD:EF01:2345:6789:ABCD:EF01:2345:6789') as v6;
+------------------+
| v6 |
+------------------+
| «ί#Eg«ί#Eg |
+------------------+
1 row in set (0.00 sec)
把二进制形式的无符号整型(128位),转换为字符串形式IPv6地址:
mysql> select inet6_aton('ABCD:EF01:2345:6789:ABCD:EF01:2345:6789') into @v6;
Query OK, 1 row affected (0.00 sec)
mysql> select inet6_ntoa(@v6) as v6s;
+-----------------------------------------+
| v6s |
+-----------------------------------------+
| abcd:ef01:2345:6789:abcd:ef01:2345:6789 |
+-----------------------------------------+
1 row in set (0.00 sec)
总结
使用整数类型字段来存储IP既节省了存储空间,而且又提高了查询效率;唯一麻烦的一点就是需要转换一下,不方便直接阅读,不过这个小问题和它带来的性能提升来比还是可以忍受的。最后总结下这个使用场景吧,我一般在涉及到那些访问记录表的设计中往往会需要记录ip,此时这个ip字段我就是使用上方的方案来进行设计和使用的,希望笔者的总结的这些能够帮助到大家。