第 31 章 / 共 57 章 · 约 4 分钟
关键技术:DynamoDB 与 Cassandra
DynamoDB 和 Cassandra 都是按分区键水平扩展的键值 / 宽列存储:写入量几乎可以随节点线性增长,代价是放弃 JOIN 和灵活查询,表结构要围绕“会怎么读”来设计。两者放在一章讲,因为面试里的取舍几乎一样;区别在于一个是托管服务,一个是自己运维的无主集群。
1. 核心模型
- 分区键决定数据放在哪里:对分区键做哈希,映射到某个分区或节点。同一分区键的数据放在一起。
- 排序键决定分区内的顺序:DynamoDB 叫 sort key,Cassandra 叫 clustering key。“某个会话按时间倒序取 50 条”就是一个分区内的范围扫描,很快。
- 一个查询一张表:没有 JOIN,要支持新的查询方式,通常是再建一个按另一种键组织的表或二级索引,把数据冗余写两份。
DynamoDB 特有的
- 全托管,按读写容量(预置或按需)计费;单条 item 最大 400 KB。
- 每个物理分区大约支持每秒 3000 个读容量单位和 1000 个写容量单位,热点分区会被限流(自适应容量能缓解,但不能根治)。
- 默认最终一致读,可以选强一致读(只对主表,不对 GSI)。
- 条件写(
ConditionExpression)做乐观锁;事务 API 一次最多 100 个 item。 - GSI(全局二级索引)是异步维护的,有延迟;Streams 提供 24 小时内的变更流;TTL 自动删除过期 item。
Cassandra 特有的
- 无主(leaderless):一致性哈希环加虚拟节点,每份数据复制到 N 个节点(常见 RF=3)。
- 每次读写可以选一致性级别:
ONE、QUORUM、LOCAL_QUORUM、ALL。读写都用 QUORUM 时 R + W > N,能读到最近一次已确认的写入。 - 存储引擎是 LSM 树:先写内存表和提交日志,再刷成 SSTable,后台合并(compaction),所以写入很快。
- 冲突按时间戳“后写者胜”;删除写的是墓碑(tombstone),大量删除会拖慢读取。
- 轻量事务(
IF NOT EXISTS)基于 Paxos,比普通写入慢好几倍,只在必要时用。
2. 什么时候用
- 写多、按 key 读:聊天消息、操作日志、IoT 时序、位置轨迹、活动流。
- 规模大且可预测:数据量几十 TB 以上、吞吐要随业务线性扩展。
- 需要稳定的低延迟键值访问:会话、购物车、用户配置(DynamoDB 尤其省心)。
- 多数据中心写入:Cassandra 的多 DC 复制配合
LOCAL_QUORUM,每个机房本地读写。
3. 什么时候不适合
- 查询方式还不确定:临时查询、多维筛选、聚合报表都很难做,PostgreSQL 更合适。
- 跨多条记录的强一致事务:扣库存同时写订单、转账两边同时改,关系数据库更自然;DynamoDB 事务有数量和成本限制,Cassandra 基本没有。
- 频繁更新同一行或大量删除:Cassandra 的墓碑和 LWW 让这类负载很难受。
- 小规模:几十 GB 的数据,用宽列存储只会增加建模成本。
4. 关键限制和数量级
| 维度 | DynamoDB | Cassandra |
|---|---|---|
| 单条大小 | item 最大 400 KB | 单个值建议远小于 1 MB |
| 分区大小 | 单个分区键下数据无硬上限,但吞吐受单分区限制 | 单个分区建议控制在 100 MB 以内,太大读写和修复都慢 |
| 单分区吞吐 | 约 3000 RCU / 1000 WCU 每秒 | 取决于节点,热分区会压垮持有它的几个副本 |
| 一致性 | 默认最终一致,可选强一致读 | 每次请求可调,QUORUM 读写可读到最新确认写入 |
| 扩容 | 自动拆分区 | 加节点后数据在环上重新分布 |
5. 面试中常被追问的点
- 分区键怎么选? 让访问均匀分布,同时让最常见的查询只打到一个分区。聊天消息用
(conversation_id, month)做分区键,既保证一个会话的消息在一起,又限制单个分区的大小。 - 热点分区怎么办? 写入时给 key 加后缀(
item#0..item#9)打散,读时并发查 10 个再合并;或者在前面加缓存。 - 需要按另一个字段查? DynamoDB 建 GSI(接受最终一致);Cassandra 一般再建一张反向表,由应用同时写两张表,或者用 CDC 同步。
- 计数器怎么做? Cassandra 有 counter 类型但不是幂等的,重试会多加;DynamoDB 用
UpdateItem ADD原子加。要求精确时,按事件写明细,再异步聚合。 - 怎么实现乐观锁? DynamoDB 用版本号加
ConditionExpression;Cassandra 用轻量事务IF version = ?,注意性能代价。
6. 和其他技术的取舍
| 选项 | 适合 | 代价 |
|---|---|---|
| DynamoDB | 托管、免运维、键值访问 | 访问模式要提前设计;云厂商锁定;热点分区限流 |
| Cassandra / ScyllaDB | 自己掌控的超大写入量、多数据中心 | 运维重,一致性和墓碑需要经验 |
| PostgreSQL | 事务、约束、灵活查询 | 写入受单主限制 |
| Bigtable / HBase | 超大有序宽表、扫描 | 生态和运维各有门槛 |
7. 本站案例
- 案例 4:即时通讯:消息表以
(conv_id, month_bucket)为分区键、seq为排序键。 - 案例 9:分布式键值存储:从零设计一个 Dynamo 风格的无主存储,讲清 quorum、向量时钟和反熵修复。
- 案例 1:短链接服务:短码到长链接的映射用键值存储,条件写防止冲突。