为什么建议MySQL列属性尽量NOT NULL

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 为什么建议MySQL列属性尽量NOT NULL

本文测试环境:MySQL5.7,Engine=InnoDB,charset=utf8。


先看一段官方的话:很多表都包含可为null(空值)的列,即使应用程序并不需要保存null也是如此,这是因为可为null是列的默认属性。通常情况下最好指定为Not null,除非真的需要存储null值。


如果查询中包含可为null的列,对MySQL来说更难优化,因为可为null的列使得索引、索引统计和值比较都更复杂。


可为null的列会使用更多的存储空间,在MySQL里也需要特殊处理。当可为null的列被索引时,每个索引记录需要一个额外的字节。在MyISAM里甚至还可能导致固定大小的索引(例如只有一个整数列的索引)变成可变大小的索引。


通常把可为null的列改为not null带来的性能提升比较小,所以调优时没有必要首先在现有schema中查找并修改掉这种情况,除非确定这会导致问题。但是如果计划在列上建索引,就应该尽量避免设计为可为null的列。


当然也有例外,值得一提的是,InnoDB使用单独的位(bit)存储null值,所以对于稀疏数据有很好的空间效率。但这一点不适用于MyISAM。关于这一点可以参考博文认真学习InnoDB的行格式


本地创建两个表,user 和user2,表字段一样,但是user中字段user_name不允许为null,user2中user_name default null,都为user_name添加上普通索引。


user表数据:

user2表数据:


① 在使用 not in、!=查询结果集时,字段值为null的列不会出现在结果中

如下所示:

select * from user2 where user_name !='admin'


查询结果如下(没有出现ID为2的那一列):

使用not in测试同上:

select * from user2 where user_name not in
(select user_name from user2 where user_id != 1)


② 在使用null作为条件判断时,应该使用is null或is not null ,而不要使用= 或者 !=

select * from user2 where user_name is null;
select * from user2 where user_name is NOT null;


③ 使用 concat 函数拼接时,首先要对各个字段进行非 NULL 判断,否则只要任何一个字段为空都会造成拼接的结果为 NULL。

如下所示,拼接最终结果为null:

null同样不能用于算术运算:


④ 在使用count(列名)进行统计时,null不会计入统计

select count(2) from user2;--2
select count(*) from user2;--2
select count(user_name) from user2;--1


如上所示,在使用count(列名)进行统计时,null不会计入统计

count(distinct col) 计算该列除 NULL 之外的不重复行数,注意 count(distinct col1, col2) 如果其中一列全为 NULL,那么即使另一列有不同的值,也返回为 0。



⑤ NULL 列需要更多的存储空间


一般需要一个额外的字节作为判断是否为 NULL 的标志位。如果你仔细观察 user 和user2表的 key_len,会发现 user2 比user 多了一个字节

explain select * from user where user_name ='admin'

explain select * from user2 where user_name ='admin';

key_len 的长度一般跟这三个因素有关,分别是数据类型,字符编码,是否为 NULL。

因此,user2比 user1 多出的这一个字节,用于作为判断是否为 NULL 的标志位了。


⑥ is null is not null可能用不到索引

如果某列可为null,那么当使用is null 或者 is not null进行判断的时候用不到索引。可能我们在很多地方看到过这句话,那么一定是这样吗?


① 单列索引测试

如下创建表testc2,单独为c1创建索引,c1 默认为null。

CREATE TABLE `testc2` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `c1` varchar(100) DEFAULT NULL,
  `c2` varchar(100) DEFAULT NULL,
  `c3` varchar(100) DEFAULT NULL,
  `c4` varchar(100) DEFAULT NULL,
  `c5` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `testc_c1_IDX` (`c1`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4

在没有null值存在的情况下进行验证,is not null没有用到索引 ,但是is null 用到了索引。

我们修改id=1的记录中c1为null,然后再进行测试。

update testc2  set c1 = null where id=1

如下两图所示 is not null没有用到索引,is null用到了索引。

针对上面测试结果,我们可以得到如下表格。


不存在null值 存在null值
is null Using index condition Using index condition
is not null Using where Using index condition

出于好奇,我们将c1设置为not null,并填充了数据后再次进行测试,发现此时均用不到索引。

explain select *  from testc2 where c1 is not null ;
# Impossible WHERE
explain select *  from testc2 where c1 is  null ;

② 联合索引测试

CREATE TABLE `testc` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `c1` varchar(100) DEFAULT NULL,
  `c2` varchar(100) DEFAULT NULL,
  `c3` varchar(100) DEFAULT NULL,
  `c4` varchar(100) DEFAULT NULL,
  `c5` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `testc_c1_IDX` (`c1`,`c2`,`c3`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4

如上所示,我们创建联合索引,索引列可为null,但是此时没有数据为null。

如上四种情况,均用到了索引。


我们将 id =1 的记录的c2设置为null,再进行上面验证:

update testc  set c2 =null where id=1;


如上,四种情况,均用到了索引。


我们将 id =1 的记录的c1设置为null,再进行上面验证:

update testc  set c1 =null where id=1;
explain select *  from testc where c1 is null;
explain select *  from testc where c1 is not null;
explain select *  from testc where c1 ='b1' and c2 is  null;
explain select *  from testc where c1 ='b1' and c2 is not null;


可以看到,同样均使用到了索引 !

同样出于好奇(虽然没啥实际意义),我们将数据进行了填充并设置c1 not null ,然后进行测试,如下图所示,此时没有用到索引。


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
3月前
|
SQL 关系型数据库 MySQL
在 MySQL 中使用 IS NULL
【8月更文挑战第12天】
632 0
在 MySQL 中使用 IS NULL
|
3月前
|
SQL 关系型数据库 MySQL
mysql不等于<>取特定值反向条件的时候字段有null值或空值读取不到数据
对于数据库开发的专业人士来说,理解NULL的特性并知道如何正确地在查询中处理它们是非常重要的。以上所介绍的技巧和实例可以帮助你更精准地执行数据库查询,并确保数据的完整性和准确性。在编写代码和设计数据库结构时,牢记这些细节将有助于你避免许多常见的错误,提高数据库应用的质量与性能。
105 0
|
4月前
|
关系型数据库 MySQL 数据库
实时计算 Flink版产品使用问题之如何排除某个列进行同步MySQL数据
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
4月前
|
数据库 索引 关系型数据库
MySQL设计规约问题之为什么在插入数据时,必须显示指定插入的列属性
MySQL设计规约问题之为什么在插入数据时,必须显示指定插入的列属性
|
4月前
|
存储 数据库 索引
MySQL设计规约问题之什么样的属性上禁止建立索引
MySQL设计规约问题之什么样的属性上禁止建立索引
|
4月前
|
SQL 存储 索引
MySQL设计规约问题之为什么应该把字段定义为NOT NULL并且提供默认值
MySQL设计规约问题之为什么应该把字段定义为NOT NULL并且提供默认值
|
5月前
|
SQL 关系型数据库 MySQL
MySQL外键约束行为解析:CASCADE, NO ACTION, RESTRICT, SET NULL
MySQL外键约束行为解析:CASCADE, NO ACTION, RESTRICT, SET NULL
283 0
|
5月前
|
存储 关系型数据库 MySQL
深入探索MySQL的虚拟列:发展、原理与应用
深入探索MySQL的虚拟列:发展、原理与应用
|
5月前
|
关系型数据库 MySQL
MySQL中如何处理NULL值以及如何使用正则表达式
MySQL中如何处理NULL值以及如何使用正则表达式
|
24天前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
57 3
Mysql(4)—数据库索引