NoSQL 专家模式(Cassandra & DynamoDB)
概述
本技能为分布式宽列存储和键值存储(特别是 Apache Cassandra 和 Amazon DynamoDB)提供专业的心智模型和设计模式。
与 SQL(对数据实体建模)或文档存储(如 MongoDB)不同,这些分布式系统要求你先对查询建模。
适用场景
- 规模化设计:从简单的单节点数据库迁移到分布式集群。
- 技术选型:评估或使用 Cassandra、ScyllaDB 或 DynamoDB。
- 性能调优:排查现有 NoSQL 系统中的"热分区"或高延迟问题。
- 微服务:在需要高度优化读取的场景中实施"每服务一数据库"模式。
思维转变:SQL vs 分布式 NoSQL
| 特性 | SQL(关系型) | 分布式 NoSQL(Cassandra/DynamoDB) |
|---|---|---|
| 数据建模 | 对实体 + 关系建模 | 对查询(访问模式)建模 |
| 连接操作 | CPU 密集型,在读取时执行 | 预计算(反规范化),在写入时完成 |
| 存储成本 | 昂贵(最小化数据重复) | 廉价(为读取速度复制数据) |
| 一致性 | ACID(强一致性) | BASE(最终一致性) / 可调 |
| 扩展性 | 垂直扩展(更大的机器) | 水平扩展(更多节点/分片) |
黄金法则: 在 SQL 中,你设计数据模型来回答任何查询。在 NoSQL 中,你设计数据模型来高效回答特定查询。
核心设计模式
1. 查询优先建模(访问模式)
通常无法在不迁移或创建新表/索引的情况下"后续添加查询"。
流程:
- 列出所有实体(用户、订单、产品)。
- 列出所有访问模式("通过邮箱获取用户"、"按日期排序获取用户的订单")。
- 专门设计表结构,确保通过单次查找即可满足这些模式。
2. 分区键为王
数据根据**分区键(PK)**分布到物理节点上。
- 目标: 数据和流量的均匀分布。
- 反模式: 使用低基数分区键(如
status="active"或gender="m")会创建热分区,将吞吐量限制在单个节点的容量。 - 最佳实践: 使用高基数键(用户 ID、设备 ID、复合键)。
3. 聚类键 / 排序键
在分区内,数据在磁盘上按**聚类键(Cassandra)或排序键(DynamoDB)**排序。
- 这允许高效的范围查询(如
WHERE user_id=X AND date > Y)。 - 它实际上为特定的检索需求预排序了数据。
4. 单表设计(邻接表)
主要用途:DynamoDB(但概念适用于其他场景)
在一个表中存储多种实体类型,以实现预连接读取。
| PK(分区键) | SK(排序键) | 数据字段... |
|---|---|---|
USER#123 |
PROFILE |
{ name: "Ian", email: "..." } |
USER#123 |
ORDER#998 |
{ total: 50.00, status: "shipped" } |
USER#123 |
ORDER#999 |
{ total: 12.00, status: "pending" } |
- 查询:
PK="USER#123" - 结果: 通过一次网络请求获取用户资料和所有订单。
5. 反规范化与数据复制
不要害怕在多个表中存储相同数据以满足不同的查询模式。
- 表 A:
users_by_id(PK:uuid) - 表 B:
users_by_email(PK:email)
权衡:你必须管理跨表的数据一致性(通常使用最终一致性或批量写入)。
具体指导
Apache Cassandra / ScyllaDB
- 主键结构:
((分区键), 聚类列) - 无连接,无聚合: 不要尝试
JOIN或GROUP BY。在单独的计数器表中预计算聚合结果。 - 避免
ALLOW FILTERING: 如果在生产环境中看到它,说明你的数据模型有问题。它意味着全集群扫描。 - 写入廉价: 插入和更新只是 LSM 树的追加操作。不必像关注读取效率那样担心写入量。
- 墓碑: 删除操作是昂贵的标记。避免在标准表中使用高速删除模式(如队列)。
AWS DynamoDB
- GSI(全局二级索引): 使用 GSI 创建数据的替代视图(如"按日期搜索订单"而非按用户)。
- 注意: GSI 是最终一致性的。
- LSI(本地二级索引): 在同一分区内以不同方式排序数据。必须在创建表时创建。
- WCU / RCU: 理解容量模式。单表设计有助于优化消耗的容量单位。
- TTL: 使用 Time-To-Live 属性自动过期旧数据(免费删除),无需创建墓碑。
专家检查清单
在最终确定 NoSQL schema 之前:
- 访问模式覆盖: 每个查询模式是否都映射到特定的表或索引?
- 基数检查: 分区键是否有足够的唯一值来均匀分散流量?
- 分区拆分风险: 对于任何单个分区(如单个用户的订单),它是否会无限增长?(如果 > 10GB,你需要"分片"分区,如
USER#123#2024-01)。 - 一致性要求: 应用程序能否容忍此读取模式的最终一致性?
常见反模式
❌ 分散-聚合: 查询所有分区以查找单个项目(Scan)。
❌ 热键: 将所有"周一"数据放入一个分区。
❌ 关系型建模: 创建 Author 和 Book 表并在代码中尝试连接它们。(相反,应将书籍摘要嵌入作者表,或将作者信息复制到书籍表中)。
局限性
- 仅在任务明确匹配上述范围时使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少所需输入、权限、安全边界或成功标准,请停下来请求澄清。