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

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

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

目前大部分的开发现状来说,我们都会把字段全部设置成 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”,送你一份程序员成长进阶大礼包,欢迎勾搭。

相关推荐

【推荐】一款开源免费、美观实用的后台管理系统模版

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!项目介绍...

Android架构组件-App架构指南,你还不收藏嘛

本指南适用于那些已经拥有开发Android应用基础知识的开发人员,现在想了解能够开发出更加健壮、优质的应用程序架构。首先需要说明的是:AndroidArchitectureComponents翻...

高德地图经纬度坐标批量拾取(高德地图批量查询经纬度)

使用方法在桌面上新建一个index.txt文件,把下面的代码复制进去保存,再把文件名改成index.html保存,双击运行打开即可...

flutter系列之:UI layout简介(flutter ui设计)

简介对于一个前端框架来说,除了各个组件之外,最重要的就是将这些组件进行连接的布局了。布局的英文名叫做layout,就是用来描述如何将组件进行摆放的一个约束。...

Android开发基础入门(一):UI与基础控件

Android基础入门前言:...

iOS的布局体系-流式布局MyFlowLayout

iOS布局体系的概览在我的CSDN博客中的几篇文章分别介绍MyLayout布局体系中的视图从一个方向依次排列的线性布局(MyLinearLayout)、视图层叠且停靠于父布局视图某个位置的框架布局(M...

TDesign企业级开源设计系统越发成熟稳定,支持 Vue3 / 小程序

TDesing发展越来越好了,出了好几套组件库,很成熟稳定了,新项目完全可以考虑使用。...

WinForm实现窗体自适应缩放(winform窗口缩放)

众所周知,...

winform项目——仿QQ即时通讯程序03:搭建登录界面

上两篇文章已经对CIM仿QQ即时通讯项目进行了需求分析和数据库设计。winform项目——仿QQ即时通讯程序01:原理及项目分析...

App自动化测试|原生app元素定位方法

元素定位方法介绍及应用Appium方法定位原生app元素...

61.C# TableLayoutPanel控件(c# tabcontrol)

摘要TableLayoutPanel在网格中排列内容,提供类似于HTML元素的功能。TableLayoutPanel控件允许你将控件放在网格布局中,而无需精确指定每个控件的位置。其单元格...

想要深入学习Android性能优化?看完这篇直接让你一步到位

...

12个python数据处理常用内置函数(python 的内置函数)

在python数据分析中,经常需要对字符串进行各种处理,例如拼接字符串、检索字符串等。下面我将对python中常用的内置字符串操作函数进行介绍。1.计算字符串的长度-len()函数str1='我爱py...

如何用Python程序将几十个PDF文件合并成一个PDF?其实只要这四步

假定你有一个很无聊的任务,需要将几十个PDF文件合并成一个PDF文件。每一个文件都有一个封面作为第一页,但你不希望合并后的文件中重复出现这些封面。即使有许多免费的程序可以合并PDF,很多也只是简单的将...

Python入门知识点总结,Python三大数据类型、数据结构、控制流

Python基础的重要性不言而喻,是每一个入门Python学习者所必备的知识点,作为Python入门,这部分知识点显得很庞杂,内容分支很多,大部分同学在刚刚学习时一头雾水。...