各方面认识mysql中NULL
目录
NULL是什么
NULL就是在字段中存储NULL值(或者说没有保存任何数据,只是有标记位标记了哪行是NULL值,查询的时候展示NULL字样),空值就是字段中存储空字符('')
NULL占用空间吗
select length(NULL), length('')
+--------------+------------+-------------+
| length(NULL) | length('') |
+--------------+------------+-------------+
| NULL | 0 |
+--------------+------------+-------------+
所以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.
NULL列需要行中的额外空间来记录它们的值是否为NULL,也就是说NULL并不是占用data的空间,而是需要一个标记位记录哪一行是NULL。
如果列是not null,则不需要多一个标记位来标记行是否NULL。反之需要多出一个标记位的占用空间。
有NULL的列查询能使用索引吗
结论是有NULL列查询是会使用索引的。
WHERE子句中用了IS NULL、IS NOT NULL、!=这些条件,这些语句都会优先使用索引进行查询,而不是使用所谓的全表扫描。
这里就不举例子了,网上也很多,或者给带有NULL值的列建个二级索引,查询看看。
对于二级索引来说,索引列的值可能为NULL。那对于索引列值为NULL的二级索引记录来说,它们被放在B+树的哪里呢?答案是:放在B+树的最左边。
也就是说他们把SQL中的NULL值认为是列中最小的值。
注意:如果列的存储本来就小,出现太多NULL的话,也就是区分度不足的话,mysql优化器可能不会去走对应列的索引。也就是说是否使用索引,mysql优化器会进行估算后作出决策,所以也就是为什么有时,我们发现mysql并没有按我们的预想使用索引进行查找。
为什么又不建议使用NULL呢
- 我们在应用层编码过程不希望遇到NULL值,我们不希望输出给客户端的接口出现不确定类型的NULL值。
- 我们希望通过一个有意义的值的表示而不是NULL,增强可读性和可维护性,并能增强业务数据的规范性。
- NULL值到非NULL的更新无法做到原地更新,很容易使得索引产生页分裂,也就是索引分裂,导致索引更新性能。
- NULL值在timestamp类型下容易出问题,特别是参数explicit_defaults_for_timestamp默认为OFF时。
- NOT IN、!= 等反向条件查询在有 NULL 值的情况下返回永远为空结果,查询容易出错。
- 允许Null列需要更多的存储空间:需要一个额外字节作为判断是否为 NULL 的标记位。
- count统计会忽略NULL的记录,容易出错
注意:其实把NULL列改为NOT NULL带来的性能提示很小,这不是性能优化的重要目标,但列的类型适当合理性有利于可读、可用、健壮。
建议
实际到底什么时候使用NULL值,根据实际业务来进行区分。建议在实际开发中如果没有特殊的业务场景,可以直接使用对应类型的默认值。
参考
MySQL :: MySQL 8.0 Reference Manual :: 15.10 InnoDB Row Formats