关系型 vs NoSQL:如何选型
没有最好的数据库,只有最合适的数据库。选型前先问清楚三个问题:数据结构固定吗?需要事务吗?读写比例是多少?
| 维度 | 关系型(MySQL/PostgreSQL) | NoSQL(MongoDB/Cassandra/Redis) |
|---|---|---|
| 数据模型 | 表结构,Schema 固定,关联通过 JOIN | 文档/KV/列族/图,Schema 灵活 |
| 事务 | ACID,支持复杂多表事务 | 大多数仅支持单文档/单行原子性 |
| 查询能力 | SQL,任意条件查询,JOIN 强大 | 按 Key 查询为主,JOIN 弱或不支持 |
| 扩展方式 | 主要垂直扩展,分片复杂 | 原生水平扩展,分布式友好 |
| 一致性 | 强一致 | 通常最终一致(可调) |
| 典型用途 | 金融、订单、用户账户、任何需要ACID的场景 | 用户画像、日志、时序、社交图谱、缓存 |
NoSQL 四大家族
KV 存储
Redis / DynamoDB
Redis / DynamoDB
最简单,按 Key 精确查找。极快(内存级)。适合:Session、缓存、计数器、排行榜。
文档数据库
MongoDB / Firestore
MongoDB / Firestore
以 JSON 文档为单位存储,Schema 灵活。适合:用户资料、商品详情、内容管理(字段不固定)。
列族存储
Cassandra / HBase
Cassandra / HBase
按列族组织,写入极快,适合时序/追加场景。适合:IoT 数据、日志、消息历史、时序数据。
图数据库
Neo4j / Amazon Neptune
Neo4j / Amazon Neptune
以节点和边建模,擅长多跳关系查询。适合:社交网络、推荐系统、知识图谱、欺诈检测。
数据库分片(Sharding)
当单机数据库的存储或写入达到上限时,水平分片(Sharding)将数据分散到多个数据库节点。
分片前(单机 DB 写入瓶颈):
所有写入 ──▶ [ Single DB ] ──▶ 写 QPS 上限约 3K/s
存储上限约 5TB
分片后(3个分片):
写请求 ──▶ Shard Router
│
┌─────────┼─────────┐
▼ ▼ ▼
[DB-0] [DB-1] [DB-2]
user 0-33% user 33-66% user 66-100%
写 QPS 3倍 存储 3倍
分片策略一:范围分片(Range-Based Sharding)
按用户 ID 范围分片:
Shard 0:user_id 1 ~ 10,000,000
Shard 1:user_id 10,000,001 ~ 20,000,000
Shard 2:user_id 20,000,001 ~ 30,000,000
优点:范围查询高效(如:查所有 ID 在 1-1000 的用户)
缺点:数据分布不均(早期用户活跃,Shard 0 成热点)
新用户永远写入最后一个 Shard(写热点)
分片策略二:哈希分片(Hash-Based Sharding)
按哈希值分片:
shard_id = hash(user_id) % shard_count
user_id=1001 → hash=872341 → 872341 % 3 = 1 → Shard 1
user_id=1002 → hash=654789 → 654789 % 3 = 0 → Shard 0
user_id=1003 → hash=123456 → 123456 % 3 = 0 → Shard 0
优点:数据分布均匀,无热点问题
缺点:范围查询需要查所有 Shard(聚合查询慢)
扩容时数据迁移量大(% 变化导致大量 rehash)
分片策略三:一致性哈希(Consistent Hashing)
一致性哈希环:
0
Node A
330 60
↖ ↙
270 ── 哈希环 ── 90
↙ ↖
210 150
Node B
180
加入 Node C
普通哈希:新增/删除节点 → 几乎所有数据需要重新映射(O(N))
一致性哈希:新增/删除节点 → 只有相邻 1/N 的数据需要迁移
实际应用:
- Memcached 客户端负载均衡
- Cassandra 数据分片
- Amazon DynamoDB
- Chord DHT 分布式哈希表
虚拟节点(Virtual Node):
每个物理节点在哈希环上映射多个虚拟节点
解决数据分布不均问题(节点少时的偏斜)
读写分离与主从复制
主从复制架构:
写操作 ──▶ ┌──────────────┐
│ Primary DB │
│ (Master) │
└──────┬───────┘
│ 复制(Replication)
│ Binlog / WAL
┌─────────┼──────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Replica 1 │ │ Replica 2 │ │ Replica 3 │
│ (Slave) │ │ (Slave) │ │ (Slave) │
└────────────┘ └────────────┘ └────────────┘
读操作分散到各 Replica
复制延迟(Replication Lag)问题:
主库写入 → Binlog 传输 → 从库重放 → 有延迟(毫秒~秒)
场景:用户刚发完帖子刷新 → 读从库 → 看不到自己的帖子!
解决方案:
① 写后读(Read-Your-Writes):
用户自己的写操作之后的读,路由到主库
② 单调读(Monotonic Read):
同一用户的读请求永远路由到同一个从库
③ 关键读走主库(如支付确认页面)
分布式数据库:TiDB 与 CockroachDB
新一代分布式数据库(NewSQL)试图兼顾 SQL 的易用性和 NoSQL 的水平扩展能力。
TiDB 架构(PingCAP):
SQL 客户端(MySQL 协议兼容)
│
┌──────▼──────────────────────────┐
│ TiDB Layer │
│ SQL 解析、优化器、执行器 │
│ 无状态,可水平扩展 │
└──────┬──────────────────────────┘
│
┌──────▼──────────────────────────┐
│ PD (Placement Driver) │
│ 元数据管理、Region 调度、时钟 │
└──────┬──────────────────────────┘
│
┌──────┴──────────┬───────────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ TiKV │ │ TiKV │ │ TiKV │
│ Node 1 │ │ Node 2 │ │ Node 3 │
│(Raft副本)│ │(Raft副本)│ │(Raft副本)│
└────────┘ └────────┘ └────────┘
特性:
- 兼容 MySQL 协议,应用改造成本低
- 自动分片(Region = 96MB 的数据块)
- 基于 Raft 的强一致性
- HTAP:同时支持 OLTP 和 OLAP
| 特性 | 传统 MySQL 分片 | TiDB / CockroachDB |
|---|---|---|
| 分片透明性 | 应用层需要感知分片逻辑 | 完全透明,像单机 MySQL |
| 跨分片事务 | 非常复杂,需要 2PC 或 Saga | 原生支持分布式事务 |
| 扩容操作 | 需要手动迁移数据,高风险 | 在线自动扩容,几乎无感知 |
| 运维复杂度 | 中(熟悉 MySQL 即可) | 高(新概念多,调优复杂) |
| 性能 | 单机极致,分布式事务弱 | 单机稍低,分布式事务强 |
| 适用规模 | 数百 GB ~ 数 TB | 数 TB ~ PB 级 |
▶ 面试要点
- 分片的核心挑战:跨分片的 JOIN 和事务。设计分片键时,尽量让相关数据在同一分片(如:按 user_id 分片,用户相关的所有数据在同一片)。
- 一致性哈希的价值:扩容时只迁移 1/N 的数据,而不是全量重新分布。
- 选型口诀:有事务用关系型,海量写用列族(Cassandra),复杂查询用文档(MongoDB),关系图用图数据库。绝大多数系统的首选仍然是 PostgreSQL。