数据库表字段为何默认为 NOT NULL?
wptr33 2024-12-28 15:58 22 浏览
目前大部分的开发现状来说,我们都会把字段全部设置成 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)存储格式详解
- 变长字段长度列表:有多个字段则以逆序存储,存储格式是 16 进制,如果没有变长字段就不需要这一部分了。
- NULL 值列表:用来存储我们记录中值为 NULL 的情况,如果存在多个 NULL 值那么也是逆序存储,并且必须是 8bit 的整数倍,如果不够 8bit,则高位补 0。1 代表是 NULL,0 代表不是 NULL。如果都是 NOT NULL 那么这个就不存在了。
- ROW_ID:一行记录的唯一标志,没有指定主键的时候自动生成的 ROW_ID 作为主键。
- TRX_ID:事务 ID。
- ROLL_PRT:回滚指针。
- 每列的值。
(2)NOT NULL 对存储空间的影响
如果存在允许为 NULL 的列,就会多占用一个字节的标志位空间。
假设有一张表,只有一个字段允许为 NULL,其他字段都是 NOT NULL。当存储一条记录时,如果这个可空字段的值为 NULL,那么就需要在 NULL 值列表中进行标记,并且在记录头部占用一个额外的字节作为标志位。随着记录数量的增加,这种额外的存储空间占用也会逐渐累积。
然而,如果将所有字段都设置为 NOT NULL,就不会有 NULL 值列表和标志位的占用。例如,插入一条数据,所有字段都有确定的值,存储格式更加紧凑,不会有额外的空间浪费。
综上所述,将数据库表字段设置为 NOT NULL 可以减少存储空间的占用,使数据库的存储更加高效。特别是在处理大量数据时,这种节省空间的效果会更加明显。
·END·
希望今天的讲解对大家有所帮助,谢谢!
Thanks for reading!
作者:张张,十年研发风雨路,大厂架构师,「架构精进之路」专注架构技术沉淀学习及分享,职业与认知升级,坚持分享接地气儿的干货文章,期待与你一起成长。
关注并私信我回复“01”,送你一份程序员成长进阶大礼包,欢迎勾搭。
相关推荐
- 开发者必看的八大Material Design开源项目
-
MaterialDesign是介于拟物和扁平之间的一种设计风格,自从它发布以来,便引起了很多开发者的关注,在这里小编介绍在Android开发者当中里最受青睐的八个MaterialDesign开源项...
- 另类插这么可爱,一定是…(另类t恤)
-
IT之家(www.ithome.com):另类插图:这么可爱,一定是…OSXMavericks和Yosemite打破了苹果对Mac操作系统传统的命名方式,使用加州的某些标志性景点来替换猫...
- Android常用ADB命令(安卓adb工具是什么)
-
杀死应用①根据包名获取APP的PIDadbshellps|grep应用包名②执行kill命令...
- 微软Mac版PowerPoint测试Reading Order Pane功能
-
IT之家5月20日消息,微软公司昨日(5月19日)发布博文,邀请Microsoft365Insiders成员,测试macOS新版PowerPoint演示文稿应用,重点引入...
- Visual Studio跨平台开发实战(4):Xamarin Android控制项介绍
-
前言不同于iOS,Xamarin在VisualStudio中针对Android,可以直接设计使用者界面.在本篇教学文章中,笔者会针对Android的专案目录结构以及基本控制项进行介绍,包...
- 用云存储30分钟快速搭建APP,你信吗?
-
背景不管你承认与否,移动互联的时代已经到来,这是一个移动互联的时代,手机已经是当今世界上引领潮流的趋势,大型的全球化企业和中小企业都把APP程序开发纳入到他们的企业发展策略当中。但随着手机APP上传的...
- 谷歌P图神器来了!不用学不用教,输入一句话,分分钟给结果
-
Pine发自凹非寺量子位|公众号QbitAI当你拍照片时,“模特不好好配合”怎么办?...
- iOS文本编辑控件UITextField和UITextVie
-
记录一个菜鸟的IOS学习之旅,如能帮助正在学习的你,亦枫不胜荣幸;如路过的大神如指教几句,亦枫感激涕淋!细心的朋友可能已经注意到了,IOS学习之旅系列教程在本篇公众号的文章中,封面已经换成美女图片了,...
- Android入门图文教程集锦(android 入门教程)
-
Android入门视频教程集锦AndroidStudio错误gradientandroid:endXattributenotfound...
- 如何使用Android自定义复合视图(如何使用android自定义复合视图)
-
在最近的一个客户应用中,我遇到了一个需求,根据选定的值来生成指定数量的编辑框字段,这样用户可以输入人物信息。最初我的想法是把这些逻辑放到Fragment中,只是根据选中值的变化来向线性布局容器中增加编...
- 原生安卓开发app的框架frida常用关键代码定位
-
前言有时候可能会对APP进行字符串加密等操作,这样的话你的变量名等一些都被混淆了,看代码就可能无从下手...
- 教程10 | 三分钟搞定一个智能输入法程序
-
一案例描述1、考核知识点网格布局线性布局样式和主题Toast2、练习目标掌握网格布局的使用掌握Toast的使用掌握线性布局的使用...
- (Android 8.1) 功能与新特性(android的功能)
-
和你一起终身学习,这里是程序员AndroidAndroid8.1(API级别27)为用户和开发人员引入了各种新特性和功能。本文档重点介绍了开发人员的新功能。通过本章阅读,您将获取到以下内容:Andr...
- 怎样设置EditText内部文字被锁定不可删除和修改
-
在做项目的时候,我曾经遇到过这样的要求,就是跟百度贴吧客户端上的一样,在回复帖子的时候,在EditText中显示回复人的名字,而且这个名字不可以修改和删除,说白了就是不可操作,只能在后面输入内容。在E...
- 如何阻止 Android 活动启动时 EditText 获得焦点
-
技术背景在Android开发中,当活动启动时,EditText有时会自动获得焦点并弹出虚拟键盘,这可能不是用户期望的行为。为了提升用户体验,我们需要阻止...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
-
- 开发者必看的八大Material Design开源项目
- 另类插这么可爱,一定是…(另类t恤)
- Android常用ADB命令(安卓adb工具是什么)
- 微软Mac版PowerPoint测试Reading Order Pane功能
- Visual Studio跨平台开发实战(4):Xamarin Android控制项介绍
- 用云存储30分钟快速搭建APP,你信吗?
- 谷歌P图神器来了!不用学不用教,输入一句话,分分钟给结果
- iOS文本编辑控件UITextField和UITextVie
- Android入门图文教程集锦(android 入门教程)
- 如何使用Android自定义复合视图(如何使用android自定义复合视图)
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)