百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

数据库表字段为何默认为 NOT NULL?

wptr33 2024-12-28 15:58 30 浏览

目前大部分的开发现状来说,我们都会把字段全部设置成 NOT NULL 并且给默认值的形式。

最近在 Review 代码时候,仍然偶尔发现数据库字段很多没有设置 NOT NULL,为什么要设置成 NOT NULL 呢?

来自「高性能MySQL」中有这样一段话:

尽量避免NULL
很多表都包含可为NULL(空值)的列,即使应用程序并不需要保存NULL也是如此,这是因为可为NULL是列的默认属性。通常情况下最好指定列为NOT NULL,除非真的需要存储NULL值。
如果查询中包含可为NULL的列,对MySql来说更难优化,因为可为NULL的列使得索引、索引统计和值比较都更复杂。可为NULL的列会使用更多的存储空间,在MySql里也需要特殊处理。当可为NULL的列被索引时,每个索引记录需要一个额外的字节,在MyISAM里甚至还可能导致固定大小的索引(例如只有一个整数列的索引)变成可变大小的索引。
通常把可为NULL的列改为NOT NULL带来的性能提升比较小,所以(调优时)没有必要首先在现有schema中查找并修改掉这种情况,除非确定这会导致问题。但是,如果计划在列上建索引,就应该尽量避免设计成可为NULL的列。
当然也有例外,例如值得一提的是,InnoDB使用单独的位(bit)存储NULL值,所以对于稀疏数据有很好的空间效率。但这一点不适用于MyISAM。

本文主要对表字段为 NOT NULL 情况的应用影响汇总。


一、NOT NULL 的性能优势

数据库表字段设置为 NOT NULL 在性能方面具有诸多优势。

(1)查询优化是一个重要方面

当一列被标记为 NOT NULL 时,数据库系统可以使用这个信息来优化查询。因为系统知道这一列的值永远不会为空,所以在执行查询时可以忽略 NULL 值,从而提高查询性能。例如,在一个包含数百万条记录的大型数据库中,如果某列被设置为 NOT NULL,那么在查询这一列的值时,数据库系统可以直接忽略所有的 NULL 值,极大地提高了查询速度。

(2)NOT NULL 可以减少存储空间占用

NULL 列需要更多的存储空间,因为需要一个额外字节作为判断是否为 NULL 的标志位。如果把一些可填可不填的字段设置为 NOT NULL,就可以节省这些额外的存储空间。例如,假设有一个包含大量记录的表,其中有多个可填可不填的字段,如果这些字段都设置为 NOT NULL,那么随着记录数量的增加,节省的存储空间会非常可观。

(3)索引效率也会得到提升

索引含有空值的列很难进行查询优化,而且对表索引时不会存储 NULL 值。所以如果索引的字段可以为 NULL 值,索引的效率会下降,因为它们使得索引、索引的统计信息以及比较运算更加复杂。应该用 0、一个特殊的值或者一个空串代替 NULL 值。

综上所述,数据库表字段设置为 NOT NULL 在性能方面具有显著优势,可以提高查询速度、减少存储空间占用和提升索引效率。


二、对开发的友好性

(1)简化代码逻辑

数据库表字段设置为 NOT NULL,可以极大地简化开发人员的代码逻辑。

在实际开发中,如果字段允许为 NULL,那么开发人员在处理这些字段时,需要进行大量的空值判断。

例如,在 Java 语言中,如果实体类的某个字段允许为 NULL,那么在使用这个字段进行操作时,开发人员需要不断地进行空指针检查,以避免出现空指针异常。这不仅增加了代码的复杂度,还降低了代码的可读性和可维护性。


(2)提高数据一致性

NOT NULL 约束能够在数据库层面强制实施数据一致性约束,从而减少数据质量问题。

当数据库表中的某一列被设置为 NOT NULL 时,这意味着这一列的每一行都必须有值。这样可以确保数据的完整性和一致性,避免出现数据不完整或不一致的情况。

例如,在一个电商系统中,如果用户表中的用户名、邮箱等关键信息字段被设置为 NOT NULL,那么在用户注册和登录时,系统可以确保这些关键信息都被正确地填写,从而提高数据的质量和可靠性。此外,NOT NULL 约束还可以防止开发人员在插入或更新数据时出现错误。


三、应用注意事项

为了更好的描述应用注意事项,我们初始化原始数据,一个只有1列的表,很简单:

3.1 聚合函数

常见的聚合函数有 count、min、max、avg 以及 sum,这些聚合函数在遇到 NULL 值时,处理方式各不相同:

  • max、min、avg 和 sum 函数对 NULL 值采取的处理方式是直接忽略
  • count 函数处理 NULL 值则需要分情况进行讨论
    • count () 返回的是所有记录的总和,含有 NULL 值的记录不会被忽略,也会被计算在内;
    • count (column_name) 如果这个列名中含有一个值为 NULL,则该条记录会被忽略,此时的返回值为 count ()-1

3.2 与其他值运算规则

在数据库中,NULL 和其他任何值进行运算的结果都是 NULL,会给数据处理带来了很大的不确定性:

  • 当进行加法运算时,如果其中一个值为 NULL,那么结果也为 NULL
  • 在进行乘法、除法等其他运算时,只要有一个操作数为 NULL,结果就会是 NULL

这种特性使得在处理包含可能为 NULL 值的字段时,需要特别小心,否则很容易得到错误的结果。

假设我们有一个数据库表,其中包含两个字段 A 和 B,A 字段的值可能为 NULL,B 字段的值为固定值。当我们尝试进行 A+B 的运算时,如果 A 的值为 NULL,那么结果也会是 NULL,而不是我们期望的 B 的值。

3.3 对 distinct、group by、order by 的影响

在数据库操作中,对于 distinct 和 group by 来说,所有的 NULL 值都会被视为相等。这意味着如果在进行数据去重或者分组操作时,含有 NULL 值的记录会被归为一类。

对于 order by 来说,升序时 NULL 会排在最前。这是因为在排序过程中,数据库系统将 NULL 视为一个特殊的值,按照特定的规则进行排序。

当使用 distinct 对这个字段进行去重操作时,所有的 NULL 值会被视为同一个值,只显示一次。同样,在使用 group by 进行分组操作时,NULL 值的记录会被分到同一组中。


四、其他影响问题

4.1 索引问题

索引列中存在大量 NULL 值可能会导致索引失效,影响查询性能。因此,将数据库表字段设置为 NOT NULL 可以减少这种情况的发生,提高索引的有效性和查询性能。

(1)索引使用准确性

在网上有很多说法认为 NULL 不能使用索引,然而这种说法并不完全准确。实际上,索引列中存在 NULL 值并不意味着完全不能使用索引,只是会使数据库的优化器在选择索引时变得更加复杂。

查询条件针对的是索引列中的非 NULL 值,数据库可以使用索引进行快速查找。但是,如果查询条件涉及到 NULL 值的判断,如 IS NULL 或 IS NOT NULL,优化器可能需要考虑更多的因素来决定是否使用索引以及如何使用索引。

因为 NULL 值在数据库中被视为未知的状态,与其他具体的值不同,所以在处理包含 NULL 值的索引列时,优化器需要评估各种情况,包括索引的选择性、数据的分布等,以确定最佳的查询执行计划。这就可能导致在某些情况下,优化器选择不使用索引,而采用全表扫描等其他方式来执行查询。

(2)索引失效情况

如果索引列上存在大量的 NULL 值,数据库可能会认为使用索引并不能显著提高查询性能,因此选择不使用索引。

假设一个表,其中某个索引列上有很多 NULL 值。当进行查询时,如果查询条件涉及到这个索引列,数据库可能会发现使用索引进行查找并不能有效地减少需要扫描的数据量,因为大量的 NULL 值使得索引的选择性降低。在这种情况下,数据库可能会选择进行全表扫描,而不是使用索引。

此外,当索引列上的 NULL 值过多时,还可能影响索引的统计信息。数据库通常会根据索引的统计信息来评估查询的执行计划,如果统计信息不准确,可能会导致优化器做出错误的决策。

4.2 存储空间考虑

数据库中的一行记录在最终磁盘文件中是以行的方式来存储的,对于 InnoDB 来说,有 4 种行存储格式:REDUNDANT、COMPACT、DYNAMIC 和 COMPRESSED。

InnoDB 的默认行存储格式是 COMPACT,存储格式如下所示,虚线部分代表可能不一定会存在。

(1)存储格式详解

  1. 变长字段长度列表:有多个字段则以逆序存储,存储格式是 16 进制,如果没有变长字段就不需要这一部分了。
  2. NULL 值列表:用来存储我们记录中值为 NULL 的情况,如果存在多个 NULL 值那么也是逆序存储,并且必须是 8bit 的整数倍,如果不够 8bit,则高位补 0。1 代表是 NULL,0 代表不是 NULL。如果都是 NOT NULL 那么这个就不存在了。
  3. ROW_ID:一行记录的唯一标志,没有指定主键的时候自动生成的 ROW_ID 作为主键。
  4. TRX_ID:事务 ID。
  5. ROLL_PRT:回滚指针。
  6. 每列的值。

(2)NOT NULL 对存储空间的影响

如果存在允许为 NULL 的列,就会多占用一个字节的标志位空间。

假设有一张表,只有一个字段允许为 NULL,其他字段都是 NOT NULL。当存储一条记录时,如果这个可空字段的值为 NULL,那么就需要在 NULL 值列表中进行标记,并且在记录头部占用一个额外的字节作为标志位。随着记录数量的增加,这种额外的存储空间占用也会逐渐累积。

然而,如果将所有字段都设置为 NOT NULL,就不会有 NULL 值列表和标志位的占用。例如,插入一条数据,所有字段都有确定的值,存储格式更加紧凑,不会有额外的空间浪费。

综上所述,将数据库表字段设置为 NOT NULL 可以减少存储空间的占用,使数据库的存储更加高效。特别是在处理大量数据时,这种节省空间的效果会更加明显。


·END·


希望今天的讲解对大家有所帮助,谢谢!

Thanks for reading!

作者:张张,十年研发风雨路,大厂架构师,「架构精进之路」专注架构技术沉淀学习及分享,职业与认知升级,坚持分享接地气儿的干货文章,期待与你一起成长。

关注并私信我回复“01”,送你一份程序员成长进阶大礼包,欢迎勾搭。

相关推荐

redis的八种使用场景

前言:redis是我们工作开发中,经常要打交道的,下面对redis的使用场景做总结介绍也是对redis举报的功能做梳理。缓存Redis最常见的用途是作为缓存,用于加速应用程序的响应速度。...

基于Redis的3种分布式ID生成策略

在分布式系统设计中,全局唯一ID是一个基础而关键的组件。随着业务规模扩大和系统架构向微服务演进,传统的单机自增ID已无法满足需求。高并发、高可用的分布式ID生成方案成为构建可靠分布式系统的必要条件。R...

基于OpenWrt系统路由器的模式切换与网页设计

摘要:目前商用WiFi路由器已应用到多个领域,商家通过给用户提供一个稳定免费WiFi热点达到吸引客户、提升服务的目标。传统路由器自带的Luci界面提供了工厂模式的Web界面,用户可通过该界面配置路...

这篇文章教你看明白 nginx-ingress 控制器

主机nginx一般nginx做主机反向代理(网关)有以下配置...

如何用redis实现注册中心

一句话总结使用Redis实现注册中心:服务注册...

爱可可老师24小时热门分享(2020.5.10)

No1.看自己以前写的代码是种什么体验?No2.DooM-chip!国外网友SylvainLefebvre自制的无CPU、无操作码、无指令计数器...No3.我认为CS学位可以更好,如...

Apportable:拯救程序员,IOS一秒变安卓

摘要:还在为了跨平台使用cocos2d-x吗,拯救objc程序员的奇葩来了,ApportableSDK:FreeAndroidsupportforcocos2d-iPhone。App...

JAVA实现超买超卖方案汇总,那个最适合你,一篇文章彻底讲透

以下是几种Java实现超买超卖问题的核心解决方案及代码示例,针对高并发场景下的库存扣减问题:方案一:Redis原子操作+Lua脚本(推荐)//使用Redis+Lua保证原子性publicbo...

3月26日更新 快速施法自动施法可独立设置

2016年3月26日DOTA2有一个79.6MB的更新主要是针对自动施法和快速施法的调整本来内容不多不少朋友都有自动施法和快速施法的困扰英文更新日志一些视觉BUG修复就不翻译了主要翻译自动施...

Redis 是如何提供服务的

在刚刚接触Redis的时候,最想要知道的是一个’setnameJhon’命令到达Redis服务器的时候,它是如何返回’OK’的?里面命令处理的流程如何,具体细节怎么样?你一定有问过自己...

lua _G、_VERSION使用

到这里我们已经把lua基础库中的函数介绍完了,除了函数外基础库中还有两个常量,一个是_G,另一个是_VERSION。_G是基础库本身,指向自己,这个变量很有意思,可以无限引用自己,最后得到的还是自己,...

China's top diplomat to chair third China-Pacific Island countries foreign ministers' meeting

BEIJING,May21(Xinhua)--ChineseForeignMinisterWangYi,alsoamemberofthePoliticalBureau...

移动工作交流工具Lua推出Insights数据分析产品

Lua是一个适用于各种职业人士的移动交流平台,它在今天推出了一项叫做Insights的全新功能。Insights是一个数据平台,客户可以在上面实时看到员工之间的交流情况,并分析这些情况对公司发展的影响...

Redis 7新武器:用Redis Stack实现向量搜索的极限压测

当传统关系型数据库还在为向量相似度搜索的性能挣扎时,Redis7的RedisStack...

Nginx/OpenResty详解,Nginx Lua编程,重定向与内部子请求

重定向与内部子请求Nginx的rewrite指令不仅可以在Nginx内部的server、location之间进行跳转,还可以进行外部链接的重定向。通过ngx_lua模块的Lua函数除了能实现Nginx...