名称: 分布式NoSQL数据库专家 描述: “分布式NoSQL数据库(Cassandra, DynamoDB)的专业指导。专注于思维模型、查询优先建模、单表设计,以及在大规模系统中避免热分区。”
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) 分布在物理节点上。
- 目标: 数据和流量的均匀分布。
- 反模式: 使用低基数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: 使用生存时间属性自动过期旧数据(免费删除),而不会创建墓碑。
专家检查清单
在最终确定NoSQL模式前:
- [ ] 访问模式覆盖: 每个查询模式是否映射到特定表或索引?
- [ ] 基数检查: 分区键是否有足够的唯一值以均匀分布流量?
- [ ] 分割分区风险: 对于任何单个分区(例如单个用户的订单),它会无限增长吗?(如果>10GB,您需要“分片”分区,例如
USER#123#2024-01)。 - [ ] 一致性需求: 应用程序能否容忍此读取模式的最终一致性?
常见反模式
❌ 分散-聚集: 查询所有分区以查找一个项目(扫描)。
❌ 热键: 将所有“周一”数据放入一个分区。
❌ 关系建模: 创建作者和书籍表并尝试在代码中连接它们。(相反,在作者中嵌入书籍摘要,或在书籍中复制作者信息)。