对于一个ip地址,比如“192.168.0.110”,由于存在“.”号,所以大部分人都会用varchar类型的字段来进行存储,但是大家有没有想过,对于这样一个字符串,每个字符占用一个字节,那么就需要占用13个字节,但如果能用int存的话,就只需要占用4个字节。
有必要吗?
有人可能会说,不就差几个字节而已嘛,有必要吗?况且你如何用int类型来存ip地址呢?
首先我先回答第一个问题,我最近在研究MySQL的源码,深刻体会到了MySQL源码中对占用字节的“省吃俭用”,MySQL的本质是把数据存到磁盘并取出来用,因此相同的空间内比如16KB,如果存的有效数据越多,那么MySQL的读写性能就会越高,比如同样20个字节,就只能存1个varchar类型的ip地址,但是能存5个int类型的ip地址。
同时我也在重温《高性能MySQL》,其中有一句话我非常赞同:我们应该将更多的时间花在schema优化、索引和查询设计上。
大家往往会重视索引的设计,但忽视了schema的设计。
如何用int类型来存ip地址
再来看第二个问题,如何用int类型来存ip地址呢?
其实答案在MySQL官网中就提到了,MySQL提供了两个函数:INET_ATON()和INET_NTOA()。
INET_ATON()用来把一个ip地址字符串转成数字,比如
INET_NTOA()则相反,用来把一个数字转成ip地址字符串,比如
因此,如果我们在定义字段时,可以这么定义ip字段:
CREATE TABLE t (
id int auto_increment primary key,
ip int unsigned
);
注意,必须用int unsigned,这样能支持的数字范围上限更大,如果用的int signed,则可能会导致某些ip地址转成的数字超过了上限无法存储,无符号整型比有符号整型能表示的数字上限更大
数据库不兼容怎么办?
有人可能会想到,那难道在SQL语句中直接用INET_ATON()和INET_NTOA()这两个函数吗,其他数据库不兼容怎么办?
没关系,我们可以用Hutool中的Ipv4Util,它提供了类似的两个方法,并且实现逻辑和MySQL中的两个函数是一致的
底层算法
当然,如果你想自定义两个方法来完成这件事情,也不难,INET_ATON()的实现逻辑为:
- 按"."号切分ip地址字符串为4个部分
- 110乘以256的0次方
- 0乘以256的1次方
- 168乘以256的2次方
- 192乘以256的3次方
- 以上4个结果相加既得到最终结果
总结
要想数据库的性能高,除开配置、除开索引,不要忽略了字段类型的设计,合理的字段类型设计能达到四两拨千斤的效果。
我是大都督周瑜,最近在研究MySQL源码,并用Java手写了一个MySQL,可以关注我的公众号:IT周瑜,或直接加我微信:dadudu6789。