mysql中null占1位存储空间,通过空值位图统一管理;空字符串''不占存储且可参与统计,而null是未知值,影响计算与索引;is null查询通常全表扫描,需用生成列或函数索引优化。

MySQL中NULL到底占不占空间
它占,但只占1位(bit),不是1字节。MySQL用一个“空值位图(null bitmap)”统一标记每行中哪些列为NULL,每列对应1位——所以加10个可为NULL的字段,整行只多占2字节(ceil(10/8)),而不是每个NULL都存一个指针或标记。
真正不占存储的是空字符串 '',它的长度是0,且明确可比较、可索引、可参与COUNT()统计;而NULL是“未知值”,既不能用=判断,也不能参与SUM()或AVG()计算(结果直接变NULL)。
-
NOT NULL列完全不进这个位图,省下那1位,还让优化器更敢做谓词下推和索引跳过扫描 - 如果某列99%都是
NULL,用TEXT或BLOB类型反而更省——因为它们的值存在溢出页,主记录只存20字节指针,而NULL连指针都不存 - MyISAM引擎对
NULL更“宽容”,但InnoDB才是主流;InnoDB里NULL会破坏聚簇索引的紧凑性,尤其在ORDER BY或GROUP BY时容易触发filesort
为什么SparseColumn不是MySQL的原生概念
MySQL没有SparseColumn这个语法或存储机制——那是SQL Server的特性,用于显式声明“稀疏列”,让大量NULL值自动压缩存储并跳过索引构建。MySQL靠的是隐式位图 + 存储引擎层优化,没法像SQL Server那样用SPARSE关键字开关。
想模拟类似效果?只能靠设计:把低频非空字段单独拆到关联表,用LEFT JOIN按需加载;或者用JSON字段聚合多个稀疏属性(但会失去单字段索引能力)。
- 别在建表时写
col JSON SPARSE——MySQL会报错Unknown column option 'SPARSE' -
JSON字段里存{"phone": null},这个null是JSON值,不是SQL的NULL,不会进空值位图,但查询时要用JSON_CONTAINS()或->>'$.phone'提取,无法走B+树索引 - 如果真要“稀疏”,优先考虑垂直分表,比任何JSON或动态列都更可控、更易维护
IS NULL查询为什么慢,以及怎么加速
因为IS NULL无法使用普通B+树索引的等值查找路径——B+树默认不存NULL值(除非你显式加上INCLUDE,但MySQL不支持这个语法)。所以即使你在col_a上建了索引,WHERE col_a IS NULL大概率还是全表扫描。
- 唯一能加速
IS NULL的方法是:给该列加一个GENERATED虚拟列,比如col_a_is_null TINYINT AS (col_a IS NULL),再给它建索引 - 或者改用
COALESCE(col_a, '') = ''配合函数索引(MySQL 8.0.13+),但要注意COALESCE结果类型必须确定,否则索引失效 - 避免在
WHERE里混用IS NULL和= '',它们语义不同、执行计划完全不同,优化器很难合并处理
空值过滤时最容易忽略的边界情况
很多人以为WHERE col IS NOT NULL就能干净过滤掉所有“空”,但漏掉了三类常见干扰:
-
''(空字符串)会被保留,但它和NULL在业务上常代表同一含义,比如用户没填手机号,可能存成NULL或'' -
' '(带空格的字符串)既不是NULL也不是'',但TRIM(col)后可能为空,而TRIM()无法走索引 -
0、0.0、'0'在弱类型上下文里可能被当成“假值”,但它们是合法非空数据,IS NOT NULL照常返回TRUE
最稳妥的空值清洗逻辑是组合判断:col IS NOT NULL AND col != '' AND TRIM(col) != '',但务必注意字段类型——对数字类型用col != 0反而可能误杀真实值0。











