MySQL 生成列是什么?虚拟生成列和存储生成列有什么区别?
简化版
生成列是由表达式计算出来的列,不直接由业务写入。MySQL 中生成列分为虚拟生成列和存储生成列:虚拟列读取时计算,通常不占实际数据存储;存储列写入时计算并保存,占空间但读取更快。生成列常用于 JSON 字段提取、表达式索引、冗余计算字段和兼容复杂查询。
详细版
生成列能把某个表达式变成表中的列。例如从 JSON 字段里提取 user_id,再给这个生成列建索引,就能提升查询效率。它让“表达式计算”和“索引能力”结合起来。
虚拟生成列更省空间,但查询时要计算;存储生成列占空间,但适合读多写少、计算成本较高的场景。实际选择要看读写比例、表达式复杂度、索引需求和数据库版本支持。
面试时要注意:生成列不是随便冗余字段,它的值由数据库表达式维护,业务不能直接写错。
完整版教学
一、生成列的基本概念
生成列也叫 computed column。
它的值来自同一行其他字段的表达式。
数据库负责计算和维护。
业务插入或更新基础字段后,生成列随之变化。
这可以减少应用层冗余计算错误。
二、创建示例
CREATE TABLE events (
id BIGINT PRIMARY KEY,
payload JSON NOT NULL,
user_id BIGINT GENERATED ALWAYS AS (
JSON_UNQUOTE(JSON_EXTRACT(payload, '$.userId'))
) STORED,
INDEX idx_user_id (user_id)
);
这个例子从 JSON 中提取用户 id。
然后通过生成列建立索引。
三、虚拟列和存储列
| 类型 | 计算时机 | 存储成本 | 适合场景 |
|---|---|---|---|
| VIRTUAL | 读取时计算 | 通常更省空间 | 表达式简单、读压力不大 |
| STORED | 写入时计算并保存 | 占用空间 | 读多写少、表达式复杂 |
| 普通冗余列 | 应用维护 | 容易不一致 | 需要人工控制更新时 |
| 函数索引 | 直接索引表达式 | 版本相关 | 只为查询优化时 |
四、和普通冗余字段的区别
普通冗余字段由应用写入。
应用漏更新就会产生不一致。
生成列由数据库根据表达式计算。
它更适合确定性规则。
但复杂业务逻辑不适合都塞进生成列表达式。
五、索引价值
很多查询会对字段做函数或表达式。
如果直接在查询里计算,普通索引可能用不上。
生成列可以把表达式结果持久化成可索引列。
这在 JSON 查询、日期截断、大小写归一化等场景很常见。
生成列最大的工程价值,经常不是少写一行代码,而是让表达式查询可以走索引。
六、限制和注意事项
表达式通常要求确定性。
不能随意引用其他表数据。
字段类型要规划清楚。
大表新增存储生成列可能有 DDL 成本。
不同 MySQL 版本对索引和表达式支持也有差异。
七、误区和追问
- 误区:生成列就是应用层冗余字段。 生成列由数据库表达式维护,一致性更强。
- 误区:虚拟列一定比存储列好。 虚拟列省空间,但读取时计算可能增加成本。
- 误区:所有表达式都适合生成列。 复杂、多表、非确定性业务规则不适合。
- 追问:JSON 字段如何提高查询性能? 可以提取关键路径为生成列并建索引。
- 追问:生成列能更新吗? 通常不能直接写入,它由表达式自动生成。
- 追问:大表加生成列要注意什么? 评估 DDL 锁、回填成本、索引构建和回滚方案。
八、面试收束
先定义生成列。
再比较虚拟和存储。
最后结合 JSON 提取建索引说明真实价值。