在 字节2面:为了性能,你会违反数据库三范式吗?这篇文章中,我们分析了数据库的三范式,MySQL 在 5.7 版本中正式引入了原生的 JSON 数据类型,网上很多人关于 JSON 违反了数据库第一范式的争议,那么,JSON 真对违反了数据库第一范式吗?我们来聊一聊。
一、MySQL的JSON
MySQL 在 5.7 版本中正式引入了原生的 JSON 数据类型,接下来我们来详细地介绍 JSON相关的信息。
1. MySQL 5.7 中的 JSON 支持
发布日期:MySQL 5.7 正式发布于 2015 年10月。
JSON 数据类型:在此版本中,MySQL 引入了 JSON 类型,允许开发者在表中存储和操作 JSON 格式的数据。这不仅提高了处理半结构化数据的效率,还带来了更多的灵活性。
功能特性:
- 验证:MySQL 会自动验证存储在 JSON 列中的数据是否为有效的 JSON 格式。
- 高效存储:JSON 数据以二进制格式存储,优化了存储空间和访问速度。
- 内置函数:MySQL 5.7 提供了一系列与 JSON 操作相关的函数,如 JSON_EXTRACT、JSON_SET、JSON_ARRAY 等,方便对 JSON 数据进行查询和修改。
- 索引支持:虽然初期对 JSON 索引的支持有限,但通过生成虚拟列并在这些列上创建索引,可以提高查询性能。
2. 后续版本的改进
MySQL 8.0:在随后的 MySQL 8.0 版本中,JSON 支持得到了进一步增强,包括:
- 更丰富的 JSON 函数:新增了更多操作 JSON 数据的函数,如 JSON_TABLE 等。
- 性能优化:提升了 JSON 数据的处理性能,特别是在大规模数据集上的表现。
更好的与 SQL 标准的兼容性。
为了更好地理解 MySQL 的 JSON 数据类型,下面给出了一个使用 JSON 数据类型的简单示例。
CREATE TABLEusers (
idINT AUTO_INCREMENT PRIMARY KEY,
nameVARCHAR(100),
attributesJSON
);
INSERTINTOusers (name, attributes) VALUES
('Alice', '{"age": 30, "city": "New York"}'),
('Bob', '{"age": 25, "city": "Los Angeles"}');
-- 查询 JSON 数据
SELECTname, JSON_EXTRACT(attributes, '$.city') AS city FROMusers;
二、JSON 是否违反第一范式?
在关系数据库设计中,第一范式(1NF)要求每个表的每个字段都包含原子性(不可再分)的值。这意味着每个字段只能存储单一值,不能包含集合、数组或其他复杂的数据结构。MySQL 的 JSON 数据类型允许在一个字段中存储复杂的嵌套数据结构,这在某些情况下可能违反 1NF,但在其他情况下又可能不违反。下面我们将分别举例来说明。
1. 违反第一范式
为了说明 JSON违反1NF,这里以存储多个电话号码为例。假设我们有一个 users 表,每个用户可能有多个电话号码。如果我们将所有电话号码存储在一个 JSON 字段中,就违反了 1NF 的原子性要求。
CREATE TABLE users_conflict (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100),
phone_numbers JSON
);
INSERT INTO users_conflict (name, phone_numbers) VALUES
('Alice', '["123-4567", "234-5678"]'),
('Bob', '["345-6789"]');
问题分析:
- 非原子性:phone_numbers 字段中包含了一个数组,存储了多个电话号码,违反了每个字段只能包含单一值的要求。
- 数据冗余与一致性:查询特定电话号码或更新某个电话号码变得复杂,且难以利用关系数据库的约束(如唯一性)来保证数据的一致性。
当我们要查询所有包含电话号码 "123-4567" 的用户时,SQL语句如下:
SELECT name
FROM users_conflict
WHERE JSON_CONTAINS(phone_numbers, '"123-4567"', '$');
虽然 MySQL 提供了 JSON 函数,但这种查询复杂度高于标准的关系型查询,并且性能可能较低。
2. 不违反第一范式
为了说明 JSON 不违反1NF,这里以存储可选的、结构化的属性为例。假设我们有一个 products 表,其中大部分产品都有固定的属性(如 id、name、price),但某些产品可能有额外的可选属性(如 dimensions、manufacturer_details)。这些可选属性可以存储在一个 JSON 字段中,而不会违反 1NF。
CREATE TABLE products_no_conflict (
idINT AUTO_INCREMENT PRIMARY KEY,
nameVARCHAR(100),
price DECIMAL(10,2),
attributesJSON
);
INSERTINTO products_no_conflict (name, price, attributes) VALUES
('Laptop', 999.99, '{"dimensions": {"width": 35, "height": 2, "depth": 25}, "manufacturer_details": {"name": "TechCorp", "warranty": "2 years"}}'),
('Smartphone', 599.99, '{"color": "black", "storage": "128GB"}'),
('Book', 19.99, NULL);
问题分析:
- 维持原子性:attributes 字段用于存储可选的、结构化的额外信息。每个 attributes 字段本身被视为一个单一的 JSON 值,符合 1NF 的原子性要求。
- 数据灵活性:不需要为每种可能的属性创建单独的列,保持了表结构的简洁性。
- 查询与维护:尽管某些查询可能需要使用 JSON 函数,但由于这些属性是附加的、可选的,不会影响表的主要结构和核心数据的完整性。
当我们要查询所有宽度大于 30 的产品时,SQL语句如下:
SELECT name, attributes
FROM products_no_conflict
WHERE JSON_EXTRACT(attributes, '$.dimensions.width') > 30;
虽然这种查询依赖于 JSON 函数,但由于 attributes 字段仅包含相关的附加信息,主表结构依然保持了 1NF 的原子性。
三、总结
本文,我们分析了 MySQL 的 JSON 数据类型是否违反了数据库的第一范式(1NF),通过全文的分析,我们可以知道:JSON 是否违反 1NF 取决于具体的应用需求和数据模型设计:
- 与 1NF 冲突:当 JSON 字段用于存储多值集合(如数组、重复组)时,会违反 1NF 的原子性要求。例如,将多个电话号码存储在一个 JSON 字段中。
- 不与 1NF 冲突:当 JSON 字段用于存储单一的结构化对象,即使该对象内部包含多个键值对,也可以视为一个原子值,从而不违反 1NF。例如,存储产品的可选属性或配置信息。
需要注意的是,虽然在某些情况下使用 JSON 字段不会直接违反 1NF,但过度依赖 JSON 可能会带来查询复杂性、性能问题和数据一致性维护的挑战。因此,在设计数据库时,应权衡使用 JSON 字段的优势与潜在的规范化冲突,确保数据模型的可靠性和可维护性。