数据架构
CAP、NoSQL 与数仓。
数据架构
这一课你将能做到
- 用 CAP 定理解释「为什么网络分区时必须在一致性和可用性之间二选一」
- 为本站设计缓存方案:选型 Redis、确定缓存模式、规避缓存穿透/击穿/雪崩
- 判断一个业务场景该用 MySQL、Redis、MongoDB 还是 Elasticsearch,并给出理由
为什么要学
数据是系统里最「重」的东西——代码可以重构,服务可以重写,但几 TB 的数据迁移一次伤筋动骨。数据架构选错,后期纠正的代价是架构中最高的。
人话:选数据库就像选仓库。MySQL 是带账本的管理严格的仓库(入库慢但账目清晰),Redis 是店门口的快取货架(拿得快但地方小、可能丢),MongoDB 是什么都往里扔的大平层(灵活但找东西费劲)。
先建立图像
本站数据架构演进:
【阶段一】 【阶段二】 【阶段三】
App ──→ MySQL(单库) App ──→ Redis ──→ MySQL 订单服务 ──→ MySQL(订单库)
(缓存) (主→从) 课程服务 ──→ MySQL(课程库)
搜索服务 ──→ Elasticsearch
行为埋点 ──→ Kafka ──→ ClickHouse
CAP 定理图解:
C(一致性)
/ \
/ \
/ \
A ────── P
可用性 分区容错性
网络分区必然发生(P 必须选),所以只能选 CP 或 AP:
- CP:ZooKeeper、HBase、etcd —— 宁可不可用,不能不一致
- AP:Cassandra、DynamoDB、Redis 主从 —— 宁可旧数据,不能不可用
跟着做
步骤 1:用 ACID 分析订单场景
本站订单表设计:
CREATE TABLE orders (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
course_id BIGINT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
version INT NOT NULL DEFAULT 0, -- 乐观锁
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user_status (user_id, status)
) ENGINE=InnoDB;
ACID 逐条落地:
- 原子性:下单 = 插订单 + 扣库存,用本地事务包住
- 一致性:
status只能是枚举值,用 CHECK 或应用层校验 - 隔离性:默认 REPEATABLE READ,热点行用乐观锁 version 字段
- 持久性:
innodb_flush_log_at_trx_commit=1, redo log 落盘
步骤 2:缓存三大经典问题实战
本站课程详情接口(QPS 500)加 Redis 缓存:
// 缓存穿透:查不存在的课程 id,请求打到 DB
public Course getCourse(long id) {
String key = "course:" + id;
Course c = redis.get(key);
if (c != null) return c;
c = db.findById(id);
// 关键:空值也缓存 60 秒,防穿透
redis.set(key, c == null ? NULL_PLACEHOLDER : c,
c == null ? 60 : 3600);
return c;
}