关系型数据库 vs 非关系型数据库

一、关系型数据库概述

关系型数据库(RDBMS)基于关系模型,数据以表(Table)的形式组织,表之间有明确的关联关系,通过 SQL(Structured Query Language)进行操作。

常见产品:MySQL、PostgreSQL、Oracle、SQL Server、SQLite。

1
2
3
4
5
6
7
8
9
10
11
核心特征:
┌──────────────┐ ┌──────────────┐
│ users 表 │ │ orders 表 │
├──────────────┤ ├──────────────┤
│ id (PK) │◄─────│ user_id (FK) │
│ name │ │ product │
│ email │ │ amount │
│ created_at │ │ created_at │
└──────────────┘ └──────────────┘

数据通过外键关联,通过 JOIN 查询。

关系型数据库的核心能力

1
2
3
4
5
├── 结构化 Schema:必须先定义表结构再写入数据
├── ACID 事务:保证数据一致性(银行转账、订单扣库存)
├── 关系与 JOIN:通过外键关联多表查询
├── SQL 标准:统一的查询语言,强大的聚合和过滤
└── 成熟生态:二十多年的工具链和社区积累

二、非关系型数据库概述

非关系型数据库(NoSQL — Not Only SQL)放弃或简化了关系模型,以换取更高的性能、可伸缩性和灵活性。

常见产品:MongoDB(文档型)、Redis(键值型)、HBase(列族型)、Neo4j(图型)。

主要分类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
文档型(Document Store)
代表:MongoDB、CouchDB
数据以 JSON/BSON 文档存储,支持嵌套结构
适用:内容管理、日志、配置

键值型(Key-Value Store)
代表:Redis、DynamoDB
数据以键值对存储,性能最高
适用:缓存、会话、计数器

列族型(Column Family)
代表:HBase、Cassandra
数据以列族存储,适合宽表
适用:时序数据、大数据分析

图数据库(Graph DB)
代表:Neo4j、ArangoDB
数据以节点和边存储
适用:社交关系、推荐系统

三、核心对比

数据模型

1
2
3
4
5
6
7
8
9
10
11
12
关系型:
┌──────────┬──────────┐
│ name │ skills │ 扁平结构,需拆多表
├──────────┼──────────┤
│ Alice │ cooking │
│ Alice │ dancing │ ← 用户和技能拆分在不同表
│ Bob │ reading │
└──────────┴──────────┘

文档型(MongoDB):
{ name: "Alice", skills: ["cooking", "dancing"] } ← 嵌入式数组
{ name: "Bob", skills: ["reading"] }

Schema(模式)

关系型非关系型(文档型)
Schema严格,先定义后写入灵活,同集合可不同结构
修改字段ALTER TABLE 可能锁表随时添加,不影响旧数据
迁移需手动执行 migration在代码层面处理兼容
1
2
3
4
// MongoDB 同一集合中可以有不同结构的文档
{ name: "Alice", age: 25 } // 有 age
{ name: "Bob" } // 无 age
{ name: "Charlie", phone: "123" } // 有 phone

事务与一致性

特性关系型(MySQL InnoDB)文档型(MongoDB)
ACID 事务✅ 完整支持✅ 4.0+ 支持多文档事务
隔离级别READ COMMITTED / REPEATABLE READsnapshot 隔离
强一致性默认默认(primary 读写)
最终一致性不支持副本集可配置

查询能力

1
2
3
4
5
6
7
-- 关系型:SQL 关联查询
SELECT u.name, o.product, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.age > 20 AND o.amount > 100
ORDER BY o.created_at DESC
LIMIT 10;
1
2
3
4
5
6
7
8
9
10
// MongoDB 聚合管道
db.users.aggregate([
{ $match: { age: { $gt: 20 } } },
{ $lookup: { from: "orders", localField: "_id", foreignField: "userId", as: "orders" } },
{ $unwind: "$orders" },
{ $match: { "orders.amount": { $gt: 100 } } },
{ $sort: { "orders.createdAt": -1 } },
{ $limit: 10 },
{ $project: { name: 1, "orders.product": 1, "orders.amount": 1 } },
]);

伸缩性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
关系型:
┌───── 垂直扩展(Scale Up)─────┐
│ 增加单机 CPU/内存/磁盘 │
│ 简单但有限制(单机天花板) │
└──────────────────────────────┘
┌───── 水平扩展(Scale Out)─────┐
│ 读写分离(主从复制) │
│ 分库分表(Sharding)实现复杂 │
└──────────────────────────────┘

文档型(MongoDB):
┌───── 水平扩展(天然支持)─────┐
│ 分片(Sharding)内置 │
│ 自动数据均衡 │
│ 对应用层透明 │
└──────────────────────────────┘

综合对比表

维度关系型(MySQL)文档型(MongoDB)键值型(Redis)
数据模型表 + 行 + 列JSON 文档键值对
Schema严格灵活
事务ACID4.0+ 多文档有限
JOIN原生支持$lookup不支持
索引B+ TreeB+ Tree哈希
水平扩展复杂(分库分表)原生分片集群模式
典型场景财务、ERP、CRM内容、日志、IoT缓存、会话
一致性偏好强一致性可调最终一致性

四、CAP 定理与选型

CAP 定理

1
2
3
4
5
6
7
8
C(Consistency)一致性:所有节点在同一时刻看到相同数据
A(Availability)可用性:每次请求都能获得非错响应
P(Partition Tolerance)分区容忍性:网络分区时系统仍能运行

定理:一个分布式系统最多只能同时满足两项。
├── CP:放弃可用性,保证一致性(传统数据库)
├── AP:放弃一致性,保证可用性(很多 NoSQL)
└── CA:现实中不存在(网络分区必然发生)

如何选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
选关系型(MySQL/PostgreSQL):
├── 数据结构稳定,不会频繁变化
├── 需要复杂 JOIN 和聚合查询
├── 事务强一致性要求高(金融、订单)
└── 数据量可预估,单机可承受

选文档型(MongoDB):
├── 数据结构灵活,迭代频繁
├── 需要嵌入式文档(如用户 + 地址)
├── 需要快速水平扩展
└── 读写性能优先于强一致性

选键值型(Redis):
├── 缓存、计数器、排行榜
├── 需要微秒级响应时间
└── 数据结构简单

混合使用(最常见):
├── MySQL 存核心业务数据(订单、用户)
├── Redis 做缓存和会话
└── MongoDB 存日志、配置、内容

NoSQL 并非”不要 SQL”

1
2
3
4
5
6
NoSQL 最初的含义是 "No SQL"(反对 SQL),
后来演变为 "Not Only SQL"(不仅仅是 SQL)。

实际上,MongoDB 的查询语言越来越像 SQL,
而 MySQL 8.0 也增加了 JSON 字段和文档存储支持。
两者在互相借鉴,而不是对立。

五、面试题

Q1: 关系型和非关系型数据库的区别

1
2
3
4
5
6
1. 数据模型:关系型用表和行,非关系型用文档/键值/图
2. Schema:关系型严格,非关系型灵活
3. 关联查询:关系型原生 JOIN,非关系型需 $lookup 或应用层关联
4. 事务:关系型 ACID 完整,非关系型支持有限
5. 扩展:关系型垂直扩展为主,非关系型原生水平扩展
6. 适用场景:关系型适合强一致性业务,非关系型适合灵活/高性能场景

Q2: 什么是反范式化设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
范式化(Normalization):
减少数据冗余,通过拆分表和关联来保持一致性。

反范式化(Denormalization):
允许冗余,将关联数据嵌入同一文档或表中,
以减少 JOIN 次数,提高读取性能。

MongoDB 天然倾向反范式化:
{ user: "Alice", orders: [{ product: "手机", amount: 5000 }] }
而不是拆成 users 和 orders 两张表再 JOIN。

取舍:
范式化 → 写入快、一致性高、无冗余
反范式化 → 读取快、减少 JOIN、更新需处理冗余

Q3: 什么时候用 MongoDB 什么时候用 MySQL

1
2
3
4
5
6
7
8
9
10
11
用 MySQL:
├── 电商订单系统(需要事务保证库存一致性)
├── 财务系统(ACID 高要求)
├── 复杂报表统计(多表 JOIN + 聚合)
└── 数据结构稳定,不频繁变化

用 MongoDB:
├── 内容管理系统(不同文章类型字段不同)
├── 日志收集系统(灵活 Schema + 高写入吞吐)
├── IoT 设备数据(快速水平扩展)
└── 原型阶段(快速迭代,字段频繁变化)