面试官:MySQL 空值字段应该保存 NULL, 还是默认值?

MySQL关系型数据库数据库

大家好,我是苏三。

使用 MySQL 数据库时,对于一个可以为空的字段,如果没有值,应该保存 NULL 还是给一个默认值呢?

多数时候我们不太注意,有时候不赋值,直接保存 NULL,有时候赋值一个业务指定的默认值。

今天来聊一聊这个话题。

1.行数据存储

MySQL 保存一行数据时,不仅仅会保存数据本身,还会保存数据相关的额外信息。InnoDB 存储引擎支持四种行格式,MySQL 5.7 版本之后,默认使用 Dynamic 行格式。看一下官网给出的 4 种格式说明:

| 行格式 | 紧凑的存储特性 | 增强的可变长度列存储 | 大索引键前缀支持 | 压缩支持 | 表空间类型支持 | 文件格式 | | --- | --- | --- | --- | --- | --- | --- | | REDUNDANT | No | No | No | No | system, file-per-table, general | Antelope or Barracuda | | COMPACT | Yes | No | No | No | system, file-per-table, general | Antelope or Barracuda | | DYNAMIC | Yes | Yes | Yes | No | system, file-per-table, general | Barracuda | | COMPRESSED | Yes | Yes | Yes | Yes | file-per-table, general | Barracuda |

DYNAMIC 和 COMPRESSED 这两种格式都是 COMPACT 的改进版,基本结构跟 COMPACT 类似,我们看一下 COMPACT 这种格式。如下图:

picture.image

我们创建一张表:

  
CREATE TABLE`t\_user` (  
`id`bigint(20) NOTNULL AUTO\_INCREMENT,  
`name`varchar(16) DEFAULTNULL,  
`email`varchar(32) DEFAULTNULL,  
`address`varchar(255) DEFAULTNULL,  
  PRIMARY KEY (`id`)  
) ENGINE=InnoDBDEFAULTCHARSET=latin1;  

插入 2 行数据,

picture.image

数据行保存格式如下图:

picture.image

变长字段宽度列表保存 变长字段非空值长度。从上图可以看到,变长字段宽度列表 存放的列宽度顺序和数据表中的列顺序相反,也就是说变长字段宽度列表逆序存放列宽度。

picture.image

如果表中所有列都是 NOT NULL 并且具有固定长度,则没有变长字段宽度列表这个部分。

同样,NULL 值列表 也是逆序保存,当该值是 NULL 时,用二进制 1 表记,否则就保存二进制 0。

picture.image

如果表中所有列都是 NOT NULL,就没有 NULL 值列表这个部分。

记录头信息 用 5 个字节保存,主要记录数据的一些信息,比如:

  • delete-flag:记录是否删除,我们知道,在 MySQL 中删除一条数据,并不会马上从磁盘上删除,而是打上删除标记,在空余时间再进行异步清理。
  • record_type:记录类型,比如普通记录、非叶子节点记录。
  • next_record:指向下一条记录的地址指针。
  • n_owned:记录该组数据的条数。

隐藏列:

  • DB_TRX_ID:修改(插入、更新或删除)这一条数据的事务 id;
  • DB_ROLL_PTR:回滚指针,指向修改前的历史版本,用于回滚操作;
  • DB_ROW_ID:当表中不定义主键时用作主键来自动生成聚簇索引。

2.NULL 处理

根据上面的分析和实际使用,如果我们把一个字段直接定义成 NOT NULL,有下面好处:

  • 节省存储空间:NULL 值虽然不会占用数据存储空间,但是需要额外 1~2 个字节保存 NULL 值列表。
  • 减少应用程序 NullPointerException 的可能性;
  • 减少统计问题:比如 count(字段)不会统计 NULL 值。
  • 对索引有好处,索引是不会保存 NULL 值的,定义成 NULL 会使索引效率下降。
  • 比较操作:字段定义成 NULL,只能使用 is null 和 is not null 进行判断,不能使用比较操作比如 =、!=、>、<(都会返回 null) 。
  • 范围操作:字段定义成 NULL,使用 in、not in 语句时会返回空结果。

当然,设置为 NULL,并不是没有好处,比如:

  • 语义清晰‌:NULL 表示“无值”或“未知”,这在逻辑上更清晰准确;
  • 灵活性‌:NULL 值更容易筛选,比如在 WHERE 子句中使用 is null 进行筛选;
  • 兼容性‌:类似 JOIN 操作,NULL 跟任何值比较都会返回 NULL,这有助于保持数据的一致性和完整性。

在实际项目开发中,我们经常会在值是 NULL 的情况下给一个默认值,比如”-“、”“、”N/A“等,这一定程度上避免了空指针,但是往往带来一些额外的问题,比如上下游系统因为默认值的不一致导致业务处理受影响。

在表设计时,我们其实没有必要过多地考虑定义成 NULL 或默认值在存储空间上的影响,更多的应该考虑系统整体设计规范、保证各子系统在设计上的一致性,这样才能让处理逻辑更加健壮。

最后欢迎加入苏三的星球,你将获得:智能天气播报AI Agent、SaaS点餐系统(DDD+多租户)、100万QPS短链系统(超过并发)、复杂的商城微服务系统(分布式)、苏三商城系统、苏三AI项目、刷题吧小程序、秒杀系统、码猿简历网站、代码生成工具等10个项目的源代码、开发教程和技术答疑。 系统设计、性能优化、技术选型、底层原理、Spring源码解读、工作经验分享、痛点问题、面试八股文等多个优质专栏。

还有1V1免费修改简历、技术答疑、职业规划、送书活动、技术交流。

扫描下方二维码,可以加入星球:

picture.image

数量有限,先到先得。 目前星球已经更新了6100+篇优质内容,还在持续爆肝中.....

星球已经被官方推荐了3次,收到了小伙伴们的一致好评。戳我加入学习,已有2100+小伙伴加入学习。

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动 NoSQL 的实践与探索
随着 NoSQL 的蓬勃发展越来越多的数据存储在了 NoSQL 系统中,并且 NoSQL 和 RDBMS 的界限越来越模糊,各种不同的专用 NoSQL 系统不停涌现,各具特色,形态不一。本次主要分享字节跳动内部和火山引擎 NoSQL 的实践,希望能够给大家一定的启发。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论