问题场景
面试官问『你为什么选 MongoDB』,资深工程师会被这种问题问翻车——因为正确答案不是『因为它快』,而是『因为我的访问模式是按 user_id 聚合读、写多读少、schema 经常变』。数据库选型不是技术信仰,是约束推导:数据模型、访问模式、一致性要求、规模、运维成本,五个轴决定一切。
反面案例多得很:Uber 2016 年从 Postgres 迁到 MySQL(因为 Postgres 复制协议的写放大),又在 2020 年自研 Schemaless 在 MySQL 上跑 KV;Pinterest 2012 年从 MongoDB 迁到 MySQL + 自家分片;Discord 2017 年从 MongoDB 迁到 Cassandra(消息存储 1 亿条/天),2023 年又迁到 ScyllaDB;Stripe 至今主库仍是 MongoDB——但他们用一套精密的应用层一致性。没有最优数据库,只有最匹配的。
需求与约束(面试必问)
- 访问模式:是『按主键点查』『按二级索引扫』『范围查询』『join 多表』还是『全表聚合』?这决定数据库类型。
- 读写比与 QPS:写 100k QPS 的场景,Postgres 单机不够(极限约 50k),需要 Cassandra / DynamoDB / 分片 MySQL。
- 数据量与增长:100GB?10TB?1PB?10TB 以上单机 OLTP 数据库基本不行,必须分片或换分布式 SQL(CockroachDB / TiDB / Spanner)。
- 一致性要求:账户余额(强一致)vs 社交点赞(最终一致)。
- 延迟要求:p99 1ms(Redis)、10ms(DynamoDB)、100ms(Postgres + index)、秒级(OLAP)。
- Schema 稳定性:业务还在快速迭代(NoSQL 灵活)还是已稳定(SQL 结构化更安全)。
- 运维能力:团队会调 Postgres 还是 Cassandra?托管(Aurora / DynamoDB)还是自建?
高层架构(典型数据栈)
graph LR
APP["Application
API / Service"]
CACHE["Redis
KV cache · 会话 · 排行榜"]
OLTP["OLTP
Postgres / MySQL · DynamoDB"]
SEARCH["Search
Elasticsearch · OpenSearch"]
QUEUE["CDC / Kafka
binlog → 下游"]
DWH["Data Warehouse
Snowflake / BigQuery · OLAP"]
LAKE["Data Lake
S3 + Iceberg · 冷数据"]
APP --> CACHE
APP --> OLTP
APP --> SEARCH
OLTP -->|binlog| QUEUE
QUEUE --> DWH
QUEUE --> SEARCH
QUEUE --> LAKE
classDef app fill:#1a2530,stroke:#64c8ff,color:#e8eef5
classDef cache fill:#1a1a30,stroke:#ffb450,color:#e8eef5
classDef oltp fill:#0e2030,stroke:#5eead4,color:#e8eef5
classDef olap fill:#2a1530,stroke:#ff7ab6,color:#e8eef5
class APP app
class CACHE cache
class OLTP,SEARCH oltp
class QUEUE,DWH,LAKE olap
现代后端往往是『多数据库共存』:OLTP 跑交易、OLAP 跑分析、Search 跑搜索、Cache 抗读
关键技术点
1. SQL vs NoSQL:不是二元对立,是数据模型选择
原理:『NoSQL』是个糟糕的命名,它其实是 4 类完全不同的数据库的总称:
| 类型 | 代表 | 数据模型 | 典型场景 |
| Relational (SQL) | Postgres, MySQL, Oracle | 行 + schema + join | 交易、订单、关系强的业务 |
| Key-Value | Redis, DynamoDB, Riak | K → V(V 可能是 JSON) | 会话、缓存、计数器 |
| Document | MongoDB, Couchbase | 嵌套 JSON 文档 | 用户画像、CMS、变 schema |
| Wide-column | Cassandra, HBase, ScyllaDB | row key + 列簇 | 时序、消息、海量写 |
| Graph | Neo4j, Neptune, TigerGraph | 节点 + 边 | 社交关系、反欺诈、知识图谱 |
| Search | Elasticsearch, OpenSearch | 倒排索引 | 全文搜索、日志、分析 |
| Time-series | InfluxDB, TimescaleDB, Prometheus | (time, tags, value) | 监控、IoT、金融行情 |
SQL 的优势:
- ACID 事务跨多行多表:转账、库存扣减、订单+支付。
- Join 多表:用户关联订单、订单关联商品,一个 query 搞定。
- Schema 强约束:错误数据进不来;NoSQL 的『灵活』就是『数据脏』的别名。
- 声明式 SQL:DB 自己优化执行计划,比你手写代码快。
NoSQL 的优势:
- 水平扩展原生:Cassandra 加节点就 OK;Postgres 加 replica 容易、加 shard 痛苦。
- Schema-less:迭代快,加字段不用 migration(但用户访问模式不定型时是优势,定型后反而是负担)。
- 极致延迟 / 吞吐:DynamoDB 单 partition 3000 RCU + 1000 WCU,p99 个位数 ms。
现实案例:
- Stripe:核心账本至今用 MongoDB(分片),但靠『应用层强一致』+ 双写校验来弥补。早期选 Mongo 是为了 schema 灵活;现在迁不动了。
- Notion:用 Postgres + 分片(480 个逻辑库),所有 block 在一张表上,靠 block_id 哈希分片。
- Discord:消息存储 2017 用 Cassandra(1B msg/day),2023 切 ScyllaDB(同协议、C++ 实现、p99 latency 降 95%)。
- Facebook:用户数据用 MySQL(分片几千个)+ TAO(graph 缓存层);广告系统用 MySQL;消息用 HBase。
- Uber:从 Postgres → MySQL → Schemaless(自建 KV on MySQL)→ Docstore(自建分布式 SQL)。
2. OLTP vs OLAP:行存 vs 列存的本质区别
原理:交易系统(OLTP)和分析系统(OLAP)的访问模式完全相反:
- OLTP:每次读写少量行(按主键)、写多、低延迟(ms)、ACID。例如『查 user 123 的订单』『扣库存』。
- OLAP:扫描海量行、读多写少、长查询(秒-分钟)、聚合统计。例如『过去 30 天按地区统计 GMV』。
这决定了存储格式:OLTP 用行存(一行所有字段在一起,便于点查整行);OLAP 用列存(同一列的值在一起,便于聚合 + 高压缩比 + SIMD)。
graph LR
subgraph Row["Row Store (Postgres / MySQL)"]
R1["[1, Alice, 30, US]"]
R2["[2, Bob, 25, UK]"]
R3["[3, Carol, 28, JP]"]
end
subgraph Col["Columnar Store (BigQuery / Snowflake)"]
C1["id: [1, 2, 3]"]
C2["name: [Alice, Bob, Carol]"]
C3["age: [30, 25, 28]"]
C4["country: [US, UK, JP]"]
end
Row -->|"SELECT * WHERE id=2 ✅"| Q1["点查快"]
Col -->|"AVG(age) GROUP BY country ✅"| Q2["聚合快 · 压缩 10x"]
classDef row fill:#0e2030,stroke:#5eead4,color:#e8eef5
classDef col fill:#2a1530,stroke:#ff7ab6,color:#e8eef5
class Row,R1,R2,R3 row
class Col,C1,C2,C3,C4 col
Trade-off:
- 行存 OLTP:✅ 点查/单行更新快、事务好做;❌ 聚合慢(要扫整行)、压缩率低。
- 列存 OLAP:✅ 聚合极快(只读需要的列)、压缩率 5-20x(同列值相似)、SIMD 友好;❌ 单行更新昂贵(要改 N 列)、不适合 OLTP。
- HTAP 混合:TiDB / SingleStore 同时支持,行存 + 列存副本,但运维与一致性复杂。Cloud 时代更常见的是『OLTP + CDC → OLAP』两套独立系统。
# 列存为什么聚合快:只读需要的列
# 一张表 100 列、10 亿行
# OLTP 行存做 SELECT AVG(salary):
# 要扫 10 亿 × 100 列的全部数据 = 海量 IO
# OLAP 列存做 SELECT AVG(salary):
# 只读 salary 这一列(10 亿 × 1 列),压缩后可能 1GB
# + SIMD 向量化执行 → 秒级返回
现实案例:
- Snowflake:列存 + 多租户、存算分离(S3 存数据、按需起 warehouse 算),是『云原生 OLAP』教科书。
- Google BigQuery:列存 + Dremel 执行引擎,扫 PB 级数据秒级返回。底层是 Capacitor 格式。
- ClickHouse:开源列存 OLAP 黑马,Yandex 开发,Cloudflare 用它存日志(30PB+,每秒插 1100 万行)。
- Airbnb:OLTP Postgres + CDC → Druid / Presto 做实时分析;夜间 Spark + S3 跑数仓。
- Databricks Lakehouse:S3 + Delta Lake(列存 Parquet + 事务日志),尝试『一份数据两种用』。
3. ACID vs BASE:一致性的两种哲学
ACID(Atomicity, Consistency, Isolation, Durability):传统 RDBMS 的承诺。BASE(Basically Available, Soft state, Eventual consistency):NoSQL 的妥协。
| ACID | BASE |
| 原子性 | 事务全成或全失败 | 无;靠 idempotent 设计 |
| 一致性 | 事务后所有人看到一致视图 | 最终一致;窗口内可能读到旧值 |
| 隔离 | SI / RR / Serializable | 通常 read committed 都难保证 |
| 持久性 | commit 落盘(fsync) | 多副本异步,少数极端情况丢 |
| 典型 | Postgres, Spanner, CockroachDB | DynamoDB(旧), Cassandra, Riak |
但 2010 年代起,这条界线在模糊:
- Spanner / CockroachDB / TiDB:分布式 SQL,靠 Paxos/Raft + TrueTime 提供 ACID + 水平扩展。
- DynamoDB 2018+:支持事务(TransactWriteItems / TransactGetItems),最多 100 个 item / 4MB。
- MongoDB 4.0+:多文档事务(性能比单文档低 10 倍,但有了)。
# Postgres SERIALIZABLE 事务(最高隔离级别)
BEGIN;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT balance FROM accounts WHERE id = 1 FOR UPDATE;
-- 如果 balance >= 100, 扣款
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;
-- ⚠️ Serializable 下如果有并发冲突, 会抛 SerializationFailure
-- 应用层要捕获并重试 (退避 + jitter)
Trade-off:
- 强一致 ACID:✅ 业务代码简单(不用想并发)、不会写错;❌ 单机有上限(Postgres 单机写 50k QPS)、跨区延迟高(Spanner 跨区写 ~100ms)。
- BASE 最终一致:✅ 极致可用性、横向扩展无上限;❌ 应用层得处理 stale read、duplicate write,开发复杂度爆炸。
- 『正确性 vs 速度』:金融转账必须 ACID;社交点赞、计数器、推荐排序可以 BASE。
现实案例:
- Google Spanner:靠 TrueTime(GPS + 原子钟,bounded clock skew)实现全球 ACID。Google Ads 后端、Cloud Spanner 都基于它。
- Amazon DynamoDB:原本严格 BASE(2007 论文),2018 加事务支持;ACID 模式 throughput 减半。
- CockroachDB:开源 Spanner 思路,靠 HLC(Hybrid Logical Clock)做跨区 ACID。DoorDash、Comcast 在用。
- TiDB:PingCAP 的 MySQL 兼容分布式 SQL,国内大量电商用它做 OLTP+OLAP。
- Cassandra:BASE 教科书,靠 quorum read/write(R + W > N)调一致性。Discord、Apple、Netflix 在用。
4. 选型决策树:Postgres / DynamoDB / Cassandra / Redis
原理:四个主流选项覆盖 90% 场景。下面是一个实用决策路径。
graph TD
Start{"需要事务 & Join?"}
Start -->|"是"| SQL_q{"数据量 > 1TB?"}
Start -->|"否"| KV_q{"延迟要求 < 1ms?"}
SQL_q -->|"否"| PG["✅ Postgres
事务 · 复杂查询 · JSONB"]
SQL_q -->|"是"| Dist_q{"全球多区?"}
Dist_q -->|"是"| Span["✅ Spanner / CockroachDB
跨区 ACID"]
Dist_q -->|"否"| TiDB["✅ TiDB / Vitess
分布式 MySQL"]
KV_q -->|"是"| Redis["✅ Redis
内存 KV · 缓存 · 排行榜"]
KV_q -->|"否"| Scale_q{"QPS > 50k 写 & 海量数据?"}
Scale_q -->|"是"| Wide_q{"托管 / 自建?"}
Scale_q -->|"否"| PG2["✅ Postgres
通常够用"]
Wide_q -->|"托管"| DDB["✅ DynamoDB
无运维 · 按访问付费"]
Wide_q -->|"自建"| Cass["✅ Cassandra / Scylla
多区写 · BASE"]
classDef sql fill:#0e2030,stroke:#5eead4,color:#e8eef5
classDef kv fill:#1a1a30,stroke:#ffb450,color:#e8eef5
classDef wide fill:#2a1530,stroke:#ff7ab6,color:#e8eef5
classDef q fill:#1a2530,stroke:#64c8ff,color:#e8eef5
class PG,PG2,Span,TiDB sql
class Redis kv
class DDB,Cass wide
class Start,SQL_q,KV_q,Dist_q,Scale_q,Wide_q q
资深工程师的默认:除非你能说出『为什么不用 Postgres』的具体原因(QPS、数据量、特殊访问模式),就用 Postgres。它有事务、JSONB(半结构化)、全文搜索(tsvector)、地理(PostGIS)、时序(TimescaleDB 扩展)、列存(Citus),几乎一站式。Notion、Instagram、Robinhood、Reddit、GitLab、Heroku 全是 Postgres 大用户。
现实案例:
- Instagram:早期 Postgres 单实例,规模化后『同一个数据库实例跑多个逻辑 shard』,按 user_id 哈希;至今主存储仍是 Postgres + Cassandra(feed 缓存)。
- Lyft:DynamoDB 跑 driver location(高写入、点查)+ Postgres 跑订单。
- Netflix:Cassandra(主)+ DynamoDB(部分)+ EVCache(Memcached fork)+ Elasticsearch。
- Shopify:MySQL Vitess(4PB+),按 shop_id 分片,单 shop 一个 pod。
- OpenAI:用户/账单 Postgres + Redis;API logs 走 ClickHouse。
扩展与优化(增长后怎么办)
- Postgres 单机顶不住:先 read replica(读写分离)→ 垂直分库(按业务拆 user / order / inventory)→ 水平分片(Citus / Vitess / 应用层)。
- 跨业务 join:CDC(Debezium 抓 binlog)→ Kafka → 物化到搜索/数仓,应用层 join 转为下游消费。
- OLAP 拖慢 OLTP:千万别在主库上跑分析 SQL;用 read replica 或 CDC 到独立 OLAP(Snowflake / ClickHouse)。
- 多区:单区 OLTP(避免分布式事务)+ 异步复制到其他区;或者上 Spanner / CockroachDB(全球 ACID 但写延迟 100ms+)。
- 冷数据归档:热数据留 OLTP(最近 90 天),冷数据走 S3 + Iceberg/Parquet,查询用 Athena/Trino。
常见陷阱(资深工程师也常翻车)
1. 用 MongoDB 当关系库用:业务一旦稳定,你会发现『没有 join』是诅咒。要么应用层做 N+1(慢),要么数据冗余(写放大、一致性恶梦)。Stripe 至今为此付代价。
2. 把 Redis 当主存储:Redis 持久化(RDB/AOF)不是设计目标,挂掉 RPO 至少几秒。账户余额这种数据,Redis 是 cache 不是 source of truth。
3. ORM 自动生成 SQL:ActiveRecord/Django ORM 一不小心就 N+1 query 或全表扫描。生产前必须 EXPLAIN。
4. 忽视访问模式优先 schema:NoSQL(尤其 DynamoDB)必须『先想清楚怎么查再设计表』。partition key 选错=重做。AWS 官方推 single-table design,但学习曲线陡。
5. Schema migration 不可逆:大表加列、改类型可能锁表几小时。生产做法:在线 DDL(pt-online-schema-change / gh-ost)、分批 backfill、双写新旧字段。
6. 跨数据库分布式事务:『写 Postgres + 写 Redis』如果一致性敏感,要用 outbox pattern(Day 7 详讲),不要寄希望于 2PC。
面试问题示例
- 你设计一个 Twitter,timeline 用什么存?为什么不全用 MySQL?
- Postgres 和 MySQL 选哪个?说出 3 个真实差异(不要说『差不多』)。
- 什么场景下你会选 Cassandra 而不是 DynamoDB?
- 解释 BASE 模型的『S』(soft state)是什么意思,给个例子。
- 用户量从 10w 到 1 亿,数据库怎么演进?分几个阶段、每个阶段瓶颈是什么。
- HTAP 数据库(TiDB)和『OLTP + CDC 到 OLAP』两套方案,怎么选?
关键资源
- 《Designing Data-Intensive Applications》Ch 2-3(Kleppmann):数据模型与存储引擎的本质讨论,本主题最权威读物。
- 《Dynamo: Amazon's Highly Available Key-value Store》(SOSP 2007)+ 《Spanner: Google's Globally Distributed Database》(OSDI 2012):NoSQL 与分布式 SQL 的两座里程碑。
- Discord 工程博客《How Discord Stores Trillions of Messages》(2023):MongoDB → Cassandra → ScyllaDB 的迁移史,含 latency 数据。
English Summary
Database selection is constraint derivation, not technology religion. Five axes determine the choice: data model (relational / KV / document / wide-column / graph / search / time-series), access pattern (point lookup vs range scan vs aggregation vs join), consistency (ACID vs BASE), scale (QPS, data size, geo distribution), and operational cost. Default to Postgres unless you have a specific reason — its versatility (JSONB, full-text, geo, time-series extensions) covers 80% of needs. Reach for DynamoDB / Cassandra when write throughput exceeds ~50k QPS or you need horizontal scale-out; Redis when sub-ms latency is required; columnar warehouses (Snowflake / BigQuery / ClickHouse) when analytical aggregation dominates. Modern stacks combine multiple stores via CDC pipelines: OLTP handles transactions, search handles full-text, warehouse handles analytics.
深入思考(点击展开答案)
1. 同一个业务实体(如 user)在 OLTP(Postgres)和 OLAP(Snowflake)里都存了,访问 OLAP 总是滞后几小时。如果产品经理要『实时数据分析』,至少 3 种方案,分别什么代价?
关键:实时分析需要『分钟级或秒级新鲜度』,传统 ETL(每日批处理)做不到。
- 方案 A:CDC + 流处理 → OLAP。Debezium 监听 Postgres binlog → Kafka → Flink/Spark Streaming → 写入 ClickHouse/Druid。新鲜度可达秒级。代价:流处理复杂、状态管理难、exactly-once 不容易;架构组件多 4-5 个。
- 方案 B:HTAP 单一数据库。TiDB / SingleStore,行存 + 列存副本并存,自动同步。SQL 一致,迁移成本低。代价:列存副本异步同步仍有 lag(亚秒~几秒);写性能比纯 OLTP 低;运维复杂;社区生态比 Postgres 小。
- 方案 C:直接在 OLTP read replica 上跑分析。开个独立 read replica(资源隔离),重 query 走这台。代价:行存做聚合慢(GROUP BY 100M 行可能要分钟);replica 资源大;查询写得不好会拖累 replication。
- 方案 D:实时物化视图(Materialize, RisingWave)。订阅 CDC 自动维护聚合结果,查询毫秒级。代价:物化视图 SQL 表达力受限,复杂查询写不出来;状态膨胀。
- 方案 E:Lambda 架构。批处理(每天)+ 实时层(增量),查询时合并。代价:两套代码、数据语义难对齐(已经被多数公司放弃)。
实战推荐:CDC + ClickHouse 是 2024 年最主流(Cloudflare、Uber、字节都用);Materialize 适合『SQL 即可表达』的分析。
2. DynamoDB 的 partition key 选『user_id』,结果有个『superuser』占了 30% 的流量,单 partition 被限流(3000 RCU 上限)。不能改 schema,怎么解?
这是经典的『hot partition』问题。DynamoDB 单 partition 硬上限:3000 RCU + 1000 WCU + 10GB。
- ① Write sharding(散列尾缀):把 superuser 的写打到多个虚拟 partition。比如 partition key 改为
user_id#{0..15},写时随机选;读时 16 个并发 query 后合并。代价:读放大 16 倍;只适合写热。
- ② 应用层缓存:superuser 的数据放 DAX(DynamoDB Accelerator,托管 cache)或 ElastiCache。读请求大部分被缓存吸收,只有 miss 才打 DDB。代价:stale 窗口、缓存一致性问题。
- ③ Adaptive Capacity(AWS 自动):DynamoDB 已经会把 hot partition 自动分裂、给更多容量。但有上限,且分裂期间 throttle 会持续几分钟。被动方案。
- ④ On-Demand 模式:切到按需计费,AWS 内部会更激进地预分配容量。代价:成本可能 2-7 倍。
- ⑤ 数据冗余 / 二级表:把 superuser 数据复制一份到独立的小表(不同 partition key),读 superuser 走小表。代价:双写、一致性;适合『极少数极热』的场景。
本质教训:DynamoDB 的『先想访问模式再设计 partition key』不是说说而已。一旦上线发现 hot key,迁移代价巨大。这也是为什么很多团队用了 DDB 几年后又迁回 Postgres + 分片。
3. 资深工程师常说『从 Postgres 开始,不行再换』。给一个具体场景,Postgres 真的不行、必须换的临界点(QPS、数据量、特性)在哪?
Postgres 单机临界点(在 AWS r6i.32xlarge / 128 vCPU / 1TB RAM 配置下):
- 写 QPS 上限约 50k-80k(含 WAL 落盘)。再高就要分片,单机 WAL 写成为瓶颈。Aurora 通过自家 storage layer 可以推到 ~200k,但 cost 翻几倍。
- 数据量上限约 5-10TB。再大维护操作(vacuum、index rebuild、备份恢复)窗口太长,autovacuum 跟不上 dead tuple 速度。
- 连接数上限约 5k(每个连接 ~10MB 内存 + 进程 fork)。所以高并发服务必须用 PgBouncer / RDS Proxy 池化。
- p99 写延迟敏感(要求 < 5ms):fsync 抖动会让 p99 飙到几十 ms;批量 vacuum / checkpoint 期间更糟。
- 跨区强一致:Postgres 的同步复制(synchronous_commit)跨区延迟会让写变成 100ms+。这种就要 Spanner / CockroachDB。
具体场景:
- IoT 时序写入 1M QPS → 必须 Cassandra/ScyllaDB/TimescaleDB 分片。
- 全球多区写入(用户在任何区都要低延迟写)→ CockroachDB / Spanner。
- 列式聚合扫 100 亿行 → ClickHouse / BigQuery。
- 毫秒级排行榜 + 实时计数器 → Redis(ZSET / HINCRBY)。
- 图遍历 5 度好友 → Neo4j / TigerGraph(Postgres recursive CTE 写得出来但性能差几个数量级)。
实战:Instagram 用 Postgres 跑到 IPO(10 亿 DAU),靠分片 + 良好访问模式设计。所以『Postgres 不够用了』往往是『设计不够好』的代名词。
4. Cassandra 的『eventual consistency』,应用层怎么处理 stale read?给两个真实场景的设计方案。
Cassandra 默认 R=1, W=1(最快但最不一致)。生产通常 R=QUORUM, W=QUORUM(R+W > N 保证一致),N=3 时即 R=2, W=2。但即使 quorum 也有 stale read 窗口(写到 2 个、复制到第 3 个之前读那一台)。
场景 1:Discord 消息列表
- 消息按 channel_id 分区、msg_id 排序。新消息可能短暂看不到。
- 设计:客户端订阅 WebSocket 推送(gateway 直接推),不依赖 DB 读。DB 读只用于历史拉取,stale 100ms 用户无感。
- 关键技巧:『写后立即推送』,绕过 DB 一致性窗口。
场景 2:电商订单状态
- 用户付款后立刻跳订单页,可能读到旧状态(pending 而不是 paid)。
- 设计:用 read-your-writes 一致性——客户端带上『最后写入的 timestamp 或 version』作为 hint,应用层确保读到 ≥ 这个版本(轮询 / 重试 / 路由到写过的副本)。
- 或者:付款回调时同步更新一份 Redis cache,用户看到的是 cache(强一致),后台异步对账 Cassandra。
通用模式:
- Read-repair:Cassandra 内置,读时如果发现副本不一致会触发后台修复。
- Hinted handoff:写时如果某副本挂了,其他节点暂存『提示』,恢复后回放。
- Lightweight Transaction(LWT / Paxos):Cassandra 提供 CAS 写(IF NOT EXISTS),代价是慢 4-10 倍。
- 应用层版本控制:写时带 version,读取后客户端做 version-aware 校验。
核心理念:BASE 系统的一致性问题要在『产品层』解决,不要妄想在 DB 层解决。Discord 不读 DB 就拿不到新消息?那就推过去。
5. 容量估算:每天 1 亿条订单,每条 1KB,保留 5 年。OLTP(Postgres)需要多少存储?SSD 还是 HDD?预算多少?
裸数据估算:
- 每天 1 亿 × 1KB = 100 GB/天
- 5 年 ≈ 1825 天 × 100 GB = 182.5 TB
实际占用(×3-5 放大因子):
- 索引:主键 + 通常 3-5 个二级索引,每个索引开销约原数据 20-40%。总索引 ≈ 1.5x 数据 = 270TB。
- WAL(write-ahead log):通常保留 7-30 天,约 10TB+。
- Vacuum bloat:Postgres MVCC 留 dead tuple,没 vacuum 干净会膨胀 20-50%。
- 备份:通常保留多个快照,本地 + 异地,再 ×3。
- 合计估算:原始数据 180TB → 实际生产存储需求 600TB-1PB。
SSD vs HDD:
- OLTP 必须 SSD(NVMe)。HDD 寻道 5-10ms,p99 SLA 没法看;NVMe p99 < 100μs。
- AWS gp3 SSD 约 $0.08/GB/月,io2 约 $0.125/GB/月。
- 1PB SSD ≈ $80k-$125k/月。一年 $1-1.5M 仅存储。
结论:单机扛不住
- 必须分片:按 user_id / shard_key 拆 32-128 个逻辑 shard。
- 冷热分离:最近 90 天热数据在 Postgres SSD(30TB),90 天-1 年温数据在 Aurora I/O Optimized(150TB),1 年以上冷数据归档到 S3 + Iceberg(HDD-tier,$0.004/GB/月 vs SSD $0.08/GB/月,省 20 倍)。
- 分层后实际 SSD 需求只剩 100-200TB,成本压到 $10k-20k/月。
这道题面试官真正想听:你会不会算放大因子(索引/WAL/备份/bloat)、知不知道分片、知不知道冷热分层省钱。只算原始 180TB 就拍 SSD 一锅端,是新手做法。