MySQL ☞ 字段类型

495 阅读5分钟

1、数值

1.1、常见的数值类型

MySQL常见的数值类型大致分为如下几种:

tinyint:      2^8       [-128,127]                [0,255]
smallint:     2^16      [-32768,32767]            [0,65535]
mediuint:     2^24      [-8388608,8388607]        [0,16777215]
int:          2^32      [-2147483648,2147483648]  [0,4294967295]
bigint:       2^64  

unsigned 无符号不允许存储负值,这大致可以使正数的上限提高一倍。

有符号和无符号使用相同的存储空间,具有相同的性能。

1.2、什么是上溢出、下溢出?

假设trade表有如下字段:
`status` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '状态',

update trade set status = 256 where id = 1001;
update trade set status = -1 where id = 1001;

取决于不同的MySQL版本和配置,上面两条 sql 在强校验模式下会直接报错;

在弱校验下会更新成功,最终的值分别被更新为255、0。

1.3、tinyint(1)和tinyint(3)有什么区别?

对大多数应用来说没有意义,他不会限制值的合法范围,只是规定了MySQL的一些交互工具来显示的字符个数,对于存储和计算来说,他们都是相同的。

1.4、其余数值类型

decimal
float
double

但是应用常见不多,比如需要存储余额、价格等浮点类型,大部分情况下也可以 * 100 转为int或bigint。

2、字符

2.1、常见的字符类型

char        一般适用于手机号、身份证号    

char(n) 若存入字符数小于n,则以空格补于其后,查询之时再将空格去掉。所以char类型存储的字符串末尾不建议存在空格,即使有也会过滤。varchar不限于此(不会过滤空格,如果入库有空格,可以明显看到)

varchar
    utf-8下:    一个汉字 = 3个字节    一个英文字符 = 1个字节
    gbk下:      一个汉字 = 2个字节    一个英文字符 = 1个字节

varchar是变长的,内部需额外使用1或2个字节用来字符串的长度(<=255使用1个,否则使用2个)

5.0版本之后,varchar(50),50指的是50个字符(4.0的时候是字节),分别可以存储50个汉字、或50个数字、或50个字母;char(50) 同理。

同样的问题,varchar是否会出现溢出问题呢?

`trade_id` varchar(20) NOT NULL DEFAULT '' COMMENT '订单号',

insert into trade set trade_id = '123456789012345678901';
ERROR 1406 (22001): Data too long for column 'trade_id' at row 1;

取决于版本和MySQL的配置,在强校验情况下,sql会直接报错;

在弱校验情况下,SQL执行成功,多余的部分被截取掉,SQL产生一个warning;

2.2、其余字符类型

binary    二进制字符串,存储的是字节码而不是字符
varbinary
blob
text
enum
set

3、日期、时间

datetime        从1001年到9999年,精度秒,格式 "2019-01-25 22:00:00"
timestamp       从1970年到2038年,精度秒,格式 "2019-01-25 22:00:00"

除了特殊情况下,尽量使用timestamp,因为它比datetime的空间效率高。

用整数存储时间戳是否会更好呢?这不会带来任何收益,具体业务具体分析。

4、你不知道的null

我们可能都听说过:尽量避免使用null,建表的时候也尽量带上not null,why?

因为null的使索引、统计、存储 都变的更加复杂,对SQL优化极其不友好。

假设某表有如下字段:
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`year` int(10) default null,

该表 insert 10条数据,其中9条数据的year字段值为null,一条值为2019,然后分别执行如下sql

select count(1) from test where year = null;
select count(1) from test where year != null;
select count(1) from test where year != 2018;
update trade set year = year+1 where id = 1;


可以发现,如果未了解过的话,还是很容易出现判断错误的情况。

5、为什么一定要有主键

给一张表设置主键,除了自增确保数据唯一,还有什么作用呢?

这个在另一篇博客有讲到MySQL的聚集索引,可以参考:juejin.cn/post/684490…

还有另外的原因:

对于无主键的表,如果在未命中索引的情况下、且为row模式的主从架构中进行delete操作,会导致备库夯住。因为MySQL的锁是加在索引上的,表中没有主键,在row模式下,每删一条数据都会做全表扫,也就是说一条delete,如果删了10条,会做10次全表扫。所以slave一直卡住;

更新聚集索引的代价极高,因为要强制InnoDB将每个被更新的行移动到新的位置;

Innodb中的每张表都要有一个聚集索引,因为聚集索引是以物理磁盘顺序来存储的,自增主键会把数据自动向后插入,避免了插入过程中的聚集索引排序问题,因为聚集索引的排序,必然会带来大范围的数据的物理移动,这里面带来的磁盘IO性能损耗是非常大的,而且还可能触发物理磁盘上的移动,于是就可能出现page分裂,表碎片横生。