缘起
大家平时在进行数据库设计的时候,如果遇到需要存储整数类型的数据的时候,通常会优先使用Int这个整数类型,在处理20亿级别的正负数值存储上,Int类型是完全能够满足日常需求的了。
但是在进行数据库建表语句书写的时候,大家经常会见到Int类型的后面会带上1个括号,里面跟上1个数值,通常要么是4,要么是11。如下:
这 int 括号里面的数值,究竟是什么意思呢?有的开发者认为,这个数值是用来限制Int类型能够存储的数字的长度的( 类似char、varchar括号数值 );有的开发者则认为,在存储相同数字的情况下,Int(4)会比Int(11)在存储上节省更多的存储空间。
那么实际情况究竟是怎样的呢?在实际的数据库设计中,究竟应该使用Int(4)还是Int(11)呢?又或者是应该什么都不写,只用默认的Int呢?
让我们开启今天的MySQL数据库之Int类型之旅。^_^
存储比较
为了方便测试Int(4)、Int(11)、以及默认的Int,在存储上是否存在差别,我们分别创建t_int_four、t_int_eleven、t_int_default表,如下:
接下来,我们向t_int_four表的my_int_four字段,插入Int( 有符号 )类型的最大值2147483647,如下:
如上图,我们看到m_int_four字段的Int(4)类型,并没有影响到Int类型最大值2147483647的插入。可见这个Int括号中的数值4,并不是对数字长度的存储进行限制的,也就是说,只要不超过Int( 有符号 )类型的最小值和最大值的范围,都是可以正确存储的。
接下来我们向t_int_eleven表的my_int_eleven字段,插入Int类型的最大值2147483647。如下:
如上图,我们看到,my_int_eleven字段的Int(11),也成功存储了Int类型的最大值2147483647。
接下来我们向t_int_default表的my_int_default字段,插入Int类型的最大值。如下:
如上图,my_int_default字段的Int类型,也成功存储了Int类型的最大值。这也就进一步证明了,Int类型括号中的数值,对该列字段的数值的存储长度是没有任何影响的,只要不超出Int类型的数值范围,都是可以被正确存储的。
这里我们发现Int类型的最大值2147483647,是一个10位长度的数字,那么my_int_eleven字段的Int(11),能否突破Int类型的最大值,存储11位长度的数值呢?
我们存入一个11位的数字,如下:
如上图,这里我们发现,即便设置了Int(11)的列字段,依然无法突破Int类型的数值范围存储限制,最终还是只允许存储Int( 有符号 )类型的有效数值范围。
那么Int(4)、Int(11)、以及默认的Int,在存储空间的占用上是否存在差别呢?是否相同位数长度的数值,Int(4)就比Int(11)节省更多的物理存储空间呢?
接下来,我们打开磁盘上的表t_int_four、t_int_eleven、t_int_default这3个表的表空间文件( 后缀名是.ibd ),打开进行对比,如下:
从上图的元数据中,我们看到不管是t_int_four的Int(4),还是t_int_eleven的Int(11),甚至是t_int_default的Int,在存储空间占用上,都是用了4个字节的空间大小,这里的FF FF FF FF,就是我们所存储的Int类型的最大值2147483647,如下:
由于我们的Int类型是有符号的,也就是能存储负数。所以这里的4294967295需要除以2,得到有符号的正数2147483647,就是Int( 有符号 )类型的最大值了。
关于有符号整数的存储及计算方法,大家可以在网上自行查询脑补,这里就不再陈述了。
ZEROFILL
经过上面的示例,我们已经知道Int类型括号中的数值,并不是控制录入数据的数值位数长度的,那么它究竟是用来干什么的呢?
在《MySQL中文参考手册》中,数值类型的列字段,都有一个叫做ZEROFILL的可选属性,如下:
按照文档中的介绍,如果一个数值类型的列字段,加上了ZEROFILL的属性后,该列类型会自动变为Unsigned( 无符号 )类型,并具备自动补0的功能。
那么究竟是什么样的效果呢?下面我们看一个示例。
如上图,在t_int_zerofill表中,我们分别创建了表示Int(4)、Int(11)、Int的3个字段:my_int_four、my_int_eleven、my_int_default。
我们在插入了1条每个字段值为数字1的数据后,发现查询出来的结果表中,自动在每个Int类型的字段列上,补了数字0。使得每一个数字列中的数值长度,正好等于该列字段Int括号中数值的长度。如下:
上面表格中,我们发现,如果Int类型的列字段中,存储的数值的位长度,小于Int括号中的数值( 后面统一叫做ZEROFILL长度 ),MySQL在查询显示的时候,就会自动在该列的存储数值的左边,进行补0。使得整个显示值的总长度,等于Int列类型的ZEROFILL长度。
如果使用的是Int默认类型,则按照Int( 无符号 )类型存储的最大数值的位长度进行补0,这里Int( 无符号 )类型的最大值为4294967295,也就是10位。所以my_int_default在显示数字1时,补9位0+1位数字(1),正好是10位。
接下来,我们插入1条各字段数值为1234的数据,如下:
如上图,这里我们看到,在插入1234之后,my_int_four的Int(4),就没有再进行补0了,因为数字1234的位长度,正好等于my_int_four列字段的ZEROFILL长度,也就是Int(4)。而其它列my_int_eleven和my_int_default,依然按照各自的ZEROFILL长度进行补0显示。
混合示例
经过上面的示例,我们知道Int类型的ZEROFILL长度参数的用法,那么其他的数值类型,是否也同样使用ZEROFILL长度参数的用法规则呢?
我们创建t_int_complex_zerofill表,并设置不同的数值类型的列字段,如下:
接下来,插入1条测试数据,分别为每一个列字段,设置该数值列类型的无符号最大数值,如下:
如上图,各种数值类型的ZEROFILL长度参数,依然对数值列类型本身的存储大小,是没有影响的。
接下来我们插入1条,各列字段数值为1的测试数据,如下:
如上图,各种数字类型的ZEROFILL长度参数都起到了预期的补0作用。
总结
本篇主要针对MySQL数据库设计中,Int类型的列字段中,括号中不同补0长度的数值设置,以及其他具备相似特性的数值列类型,进行对了对比和分析。
经过不同的示例分析,我们知道了数值列类型的补0长度的数值设置,也就是ZEROFILL的长度参数设置,对数值列类型本身的存储是没有任何影响的。
在数值列类型的字段上,如果没有显示声明“ZEROFILL”标识的话,只要存储的数值不超过该数字列类型( 有符号 )的数值范围,就都可以正确存储。也就是说Int(4)和Int(11)在存储大小上,是没有任何差别和限制的。
数值列类型的补0长度的数值设置,只有在该数值列类型的字段上,显示声明“ZEROFILL”标识之后,才会按照数值列类型的补0长度( ZEROFILL长度参数 ),进行补0显示。
那么为什么在很多MySQL的建表语句中,会经常见到没有ZEROFILL标识的Int(4)和Int(11)的写法呢?
阿K认为,这里可能主要有2种方向的考虑。
- Int(11):因为Int( 有符号 )类型的最大数值为2147483647,位长度是10。那么在存储的时候,就能从括号中看出来,在进行数据插入的时候,就不要输入11位长度的数字,比如:12345678901,就是超出范围的非法数据。用作数字插入的预警作用。
- Int(4):因为Int列类型的存储空间大小为4字节,在设计和存储的时候,就能够从括号中看出来Int列类型的占用空间大小,从而结合char、varchar等其它列的类型,方便的计算出来每条数据在插入的时候,所占用存储空间的大小,从而帮助开发者进行存储优化和数据库表优化。比如上面的t_int_complex_zerofill示例表,
我们很容易就能从各个数值列字段的ZEROFILL长度中,累加计算出,每向表中增加1条数据,就会用掉18个字节的存储空间,公式如下:
TINYINT(1) + SMALLINT(2) + MEDIUMINT(3) + INT(4) + BIGINT(8) = 18字节
在实际的数据库设计开发中,每位设计者的观点和想法都不尽相同,都有自己的设计考量。关于Int数值类型的字段设计,究竟Int(4)和Int(11)谁更好更美呢?作为开发人员的您,又是怎么看待它们的呢?