null貌似在哪里都是个头疼的问题,比如Java里让人头疼的 NullPointerException,为了避免猝不及防的空指针异常,千百年来程序猿们不得不在代码里小心翼翼的各种 if 判断,麻烦而又臃肿,为此java8 引入了Optional来避免这一问题。
下面咱们要聊的是MySQL里的null,在大量的MySQL优化文章和书籍里都提到了字段尽可能用NOT NULL,而不是NULL,除非特殊情况。但却都只给结论不说明原因。
一般,很多的字段使用null的原因大概可以总结为如下三个;
Mysql官方文档对于null字段的一个说明。
NULL columns require additional space in the rowto record whether their values are NULL. For MyISAM tables, each NULL columntakes one bit extra, rounded up to the nearest byte.
《高性能mysql第二版》中也提到,Mysql难以优化引用可空列查询,它会使索引、索引统计和值更加复杂。可空列需要更多的存储空间,还需要mysql内部进行特殊处理。可空列被索引后,每条记录都需要一个额外的字节,还能导致MYISAM中固定大小的索引变成可变大小的索引。
也就是说,不推荐使用可为null的字段是有根据的,并不是空穴来风。
NULL值到非NULL的更新无法做到原地更新,更容易发生索引分裂,从而影响性能。
Mysql的数据的存储单位都是存放在列中的,索引的组织也是排序按照从小到达排列的。
如果page2中的那一列原来为null,正好在page2放下,但是当把这一列改成非null后,page2就放不下了,那么就会导致page2的分裂。如果这个时候page3有空间还好,如果往后的所有page都没有空间了,那么就会造成后面所有的page都会分裂,更新性能受到影响。
但是注意:但把NULL列改为NOT NULL带来的性能提示很小,除非确定它带来了问题,否则不要把它当成优先的优化措施,最重要的是使用的列的类型的适当性。
NULL值在timestamp类型下容易出问题,特别是没有启用参数explicit_defaults_for_timestamp
mysql中,任何值和null的比较,都返回null,而不是true或者false。
包括null=null结果都是null,不是true
所以一旦在查询条件使用了null,而不是使用is null或者is not null将出现错误。不会返回任何值。
table3表中的数据如下:
如下4条sql语句不会返回空集合:
原因也就是mysql中,任何值和null比较后都是null,而不是true或者false,所有对于字段是null的,不会返回。
同样的,大于小于等不等号操作也是一样的。
select * from table1 where age>-1。age=null的行不会返回。
但是对于in的列表中有null的,还行效果就相当于忽略null
如果只是如上这些简单语句,可能知道mysql中null和任何值比较后都是null这个规则,那么其实都是可以避免的,但是就怕条件是动态的,如有子查询:
那么如果table1和table2的user_name都允许为null,这个sql是查不出table2中user_name为null的那些记录的。
同理
返回结果中,不会包含user_name=null的记录。注意,如果user_name上没有索引,那么其实就是因为’aaa’=null返回null,所以不会找回user_name=null的行,但是即使user_name上有索引,那么辅助索引树上,不会存字段为null(想想如果存了,没法比较大小),所以也不会找回user_name=null的行
总结:
由于mysql中,null值和任何值的比较,都会返回null,导致条件中有null的时候,返回结果和想想中的可能就不一样了。
contact()方法,如果入参有null,返回是null
table1和table3是一模一样的,唯一不同的就是user_name字段,table1是not null,而table3的user_name没有not null的约束。
Explain输出的key_len的计算规则和三个因素有关:数据类型、字符编码、是否为 NULL。
Utf8mb4编码是4字节,utf8是3字节
key_len=索引字段占用的字节数+2字节可变长字段长度(定长类型不需要)
如编码集=utf8mb6的varchar(20),ken_len=82
62=20*4 +2
key_len=索引字段占用的字节数+2字节可变长字段长度(定长类型不需要)+1(存储是否为null的标识)
如编码集=utf8mb6的varchar(20),ken_len=83
83=20*4+2+1
所以说索引字段最好不要为NULL,因为NULL会使索引、索引统计和值更加复杂,并且需要额外一个字节的存储空间。基于以上这些理由和原因,我想咱们不用 Null 的理由应该是够了
可以使用nullable value替代。这种nullable value的设计是非常常见的,甚至有些存储是不支持null的,比如ES,那么要想表达这种方式,就是采用nullable value代替(ES官方文档)。
所谓的nullable value就是对该字段没任何业务含义的值。比如age字段,可以使用任何小于等于0的值作为nullable value,年龄肯定是不可能小于等于0的。使用这种方式需要注意一下,在给前端展示的时候,要过滤掉nullable value,否则展示年龄=0,这是让用户一脸懵逼
备注:本文是转载如下文章,但是作了一些修改和补充。
原文:https://www.techug.com/post/you-should-not-use-null-in-mysql.html