自定义字段存储数据为什么要选择非关系型数据库

张开发
2026/5/4 20:41:22 15 分钟阅读
自定义字段存储数据为什么要选择非关系型数据库
需求如果有这样的需求针对各种不同的对象模型由用户自定义属性可创建、修改、删除属性然后进行具体数据值的存储。优先使用mongodb这样的非关系型数据库还是mysql这样的关系型数据库呢这是一个非常经典的EAVEntity-Attribute-Value实体-属性-值问题或自定义字段需求。结论强烈建议优先选择 MongoDB而不是 MySQL。为什么 MySQL 不擅长这个需求方案1用 EAV 模式实体-属性-值这是 MySQL 解决动态属性的常见但非常糟糕的设计-- 元数据表定义哪些属性 CREATE TABLE attributes ( id INT PRIMARY KEY, attr_name VARCHAR(50), -- color, weight, material attr_type VARCHAR(20) -- string, int, date ); -- 数据表每个属性值一行 CREATE TABLE entity_values ( entity_id INT, -- 属于哪个实体 attr_id INT, -- 哪个属性 value_string VARCHAR(500), value_int INT, value_date DATE );这个方案的致命问题问题说明查询极其复杂查询颜色红色且重量10需要多次自连接或聚合SQL 非常难写性能灾难每个属性都是一行100万实体 × 10属性 1000万行查询慢数据类型混乱所有值存为字符串无法利用数据库的类型检查、索引优化无法强制约束不能要求必须有颜色属性不能保证颜色值是有效的维护成本高动态属性变更需要修改元数据表影响所有现有数据方案2预置大量备用列如custom_field_1,custom_field_2...CREATE TABLE entities ( id INT PRIMARY KEY, name VARCHAR(100), custom_field_1 VARCHAR(255), custom_field_2 VARCHAR(255), ... custom_field_50 VARCHAR(255) );问题浪费空间、列数有限、无法处理不同数据类型、字段名无意义、查询需要知道颜色对应哪个custom_field_x。方案3用 JSON 列MySQL 5.7 支持CREATE TABLE entities ( id INT PRIMARY KEY, dynamic_attrs JSON -- 存 {color:red, weight:15} );这是 MySQL 里相对最好的方案但仍然不如 MongoDB见后面对比。为什么 MongoDB 天生适合这个需求MongoDB 的文档模型就是为这种动态 Schema场景设计的。直接存储方式// 集合 products { _id: 1, name: T恤, color: 红色, // 用户自定义属性 size: L, material: 棉 } { _id: 2, name: 笔记本电脑, cpu: i7, // 完全不同的属性集 ram: 16, storage: 512GB, color: 银色 // 也可以有相同的属性名 }MongoDB 的优势需求MongoDB 实现方式动态添加属性直接插入新字段无需修改 Schema删除属性$unset操作修改属性名/类型直接更新或迁移脚本简单查询任意属性db.products.find({color:红色})查询嵌套属性db.products.find({specs.weight: {$gt:10}})对动态属性建索引db.products.createIndex({color:1})混合数据类型原生支持同一个属性在不同文档可以是不同类型属性值校验可用 JSON Schema 验证器可选性能表现每个文档独立没有空列浪费空间查询只涉及需要的文档不需要 JOIN索引直接建在动态字段上效率高详细对比表MySQL (JSON列) vs MongoDB如果你必须在 MySQL 中使用 JSON 列对比结果如下:MySQL JSON 示例对比 MongoDB 的简洁性需求查询color红色且weight 10的记录MySQL维度MySQL (JSON列)MongoDB动态 Schema支持但需要生成虚拟列才能高效查询原生支持更自然查询动态字段需要JSON_EXTRACT()函数语法繁琐点号语法简洁索引动态字段需要创建生成列 (Generated Column)再在生成列上建索引直接创建普通索引更新部分属性JSON_SET()函数$set操作符同样原子深层嵌套操作支持但函数嵌套复杂原生点号语法数据大小JSON 存储为文本或二进制8.0有压缩BSON 二进制更紧凑事务支持✅ 完整的 ACID✅ 4.0 支持多文档事务聚合查询JSON 函数有限复杂聚合困难强大的聚合管道 (Aggregation Pipeline)学习曲线SQL JSON 函数两套语法统一的文档操作语法MySQL JSON 示例对比 MongoDB 的简洁性需求查询color红色且weight 10的记录MySQLSELECT * FROM products WHERE JSON_EXTRACT(dynamic_attrs, $.color) 红色 AND JSON_EXTRACT(dynamic_attrs, $.weight) 10;MongoDBdb.products.find({ color: 红色, weight: { $gt: 10 } })需求对price字段建索引MySQL-- 1. 先添加生成列 ALTER TABLE products ADD COLUMN price_generated DECIMAL(10,2) GENERATED ALWAYS AS (JSON_EXTRACT(dynamic_attrs, $.price)) STORED; -- 2. 再建索引 CREATE INDEX idx_price ON products(price_generated);MongoDB// 一步完成 db.products.createIndex({price: 1})特殊考虑如果必须用 MySQL比如公司强制如果被迫使用关系型数据库推荐以下方案按优劣排序首选MySQL JSON 列上面已对比次选EAV 模式仅在需要跨属性复杂关系约束时考虑比如某些属性必须关联另一个表最差备用列方案几乎不推荐但即使是 MySQL JSON 列也有明显短板无法对 JSON 内部字段设置NOT NULL或外键约束复杂聚合查询如报表性能差缺乏 MongoDB 聚合管道的灵活性最终建议场景推荐数据库用户完全自由定义属性如 CRM、低代码平台、表单生成器MongoDB✅属性数量很大每个实体几十上百个属性MongoDB✅需要频繁查询/排序/聚合这些动态属性MongoDB✅动态属性和强类型的关系数据混合混合MySQL 存固定字段 MongoDB 存动态属性公司强制只用 MySQL且动态属性很少5个MySQL JSON 列需要跨属性的复杂事务 动态属性很少MySQL JSON 列权衡一句话总结用户自定义属性、动态 Schema正是 MongoDB 文档模型最擅长的场景选择 MongoDB 可以节省 80% 的开发复杂度和维护成本。

更多文章