PostgreSQL中null值与空值分别占用多少存储空间?
NULL值存储空间占用规则详解
网上流传的「NULL值仅占用1bit存储空间」的说法只对了一半,这个描述只覆盖了NULL值的标记开销,没有考虑不同存储引擎、行格式、字段类型对应的实际占用差异,具体规则可以拆解为以下几部分:
- 基础标记位开销
所有支持NULL字段定义的关系型数据库,都会在每行数据的头部预留一块**NULL位图(Null Bitmap)**区域:只要建表时把字段设置为允许为NULL,不管这一行里这个字段实际存的是不是NULL,都会在这个位图里占1个bit的位置,用来标记当前行对应字段是否为NULL。这就是网上1bit说法的来源。
需要注意的是,这个位图是按整行分配、按字节对齐的,不是给每个NULL值单独开辟空间:比如一行里一共有7个允许为NULL的字段,NULL位图总共占1字节;如果有9个允许为NULL的字段,NULL位图就会占2字节,不会真的只留9bit的零散空间。 - 不同场景下NULL值的实际空间占用
- 主流行式数据库默认行格式(比如InnoDB的
DYNAMIC/COMPRESSED行格式、PostgreSQL默认行格式)场景下:
定长类型字段(如INT、CHAR(10)、DATETIME)存NULL时,除了位图里的1bit标记,不会占用该字段对应的固定长度存储空间;变长类型字段(如VARCHAR、TEXT、BLOB)存NULL时,除了1bit标记,既不会占用内容存储区,也不会分配变长字段必备的长度标记位,没有额外空间开销。 - 老旧行格式/存储引擎(比如InnoDB的
REDUNDANT行格式、旧版本MyISAM引擎)场景下:
定长字段存NULL时,依然会占满该类型对应的固定长度空间——比如INT字段存NULL照样占4字节,加上位图的1bit标记,总开销远大于1bit。这种设计是为了让每个字段的行内偏移量固定,简化行数据定位逻辑,不用额外做偏移量计算。 - 列式存储引擎/格式(比如ClickHouse、Parquet、ORC)场景下:
NULL值的标记是按列块单独存储的,会结合游程编码、位图压缩做优化,如果一个列块里NULL值占比很高,平均每个NULL的标记开销远小于1bit;极端情况下整列全为NULL时,几乎不占用实际数据存储空间。
- 主流行式数据库默认行格式(比如InnoDB的
- 常见认知误区
别把NULL和其他“空值类”的非NULL值搞混:比如空字符串''、数值0、无效时间戳0000-00-00都属于非NULL值,不会触发NULL位图的标记,会正常占用对应类型的存储空间。举个例子,VARCHAR类型存空字符串,至少要占1字节的长度标记位(用来标记内容长度为0),和NULL的存储开销完全不是一个量级。
内容的提问来源于stack exchange,提问作者Abhishek Bansal
相关产品推荐
相关产品推荐

