Amazon DynamoDB 表设计模式 - 单表设计与 GSI 的活用
解析 DynamoDB 的分区键设计、单表设计以及通过 GSI 实现多种访问模式的方法。
DynamoDB 概述
DynamoDB 是一种提供毫秒级延迟的全托管 NoSQL 数据库。与 RDS 不同,它是无模式的,通过分区键和排序键的组合来访问数据。作为无服务器架构的标准数据存储,通常与 Lambda 和 API Gateway 组合使用。它提供按需模式和预置模式两种容量模式。DynamoDB 以分区为单位自动扩展,每个分区最多处理 10 GB 数据和每秒 3,000 RCU / 1,000 WCU。表大小无上限,可存储数百 TB 的数据。
表设计
分区键直接影响数据的分布,因此应选择基数较高的属性(用户 ID、订单 ID)。在单表设计中,PK 添加如「USER#123」「ORDER#456」的前缀,SK 用于表示实体类型和关系。通过 Query 指定 PK,可以在一次查询中获取用户信息和订单历史。GSI 用于支持不同的访问模式(通过邮箱搜索用户、按日期范围搜索订单),每个表最多可创建 20 个。如果将日期或状态等低基数值选为分区键,会导致访问集中在特定分区的热分区问题。此时,在键上添加后缀(随机值或时间戳的一部分)来分散请求。
GSI 与单表设计
全局二级索引(GSI)使用与主表不同的分区键和排序键执行查询。在单表设计中,将多个实体(用户、订单、商品)存储在一个表中,通过 GSI 重载来支持多种访问模式。PK 设为 'USER#123',SK 设为 'ORDER#456' 等前缀,使用 begins_with 条件筛选实体。稀疏索引仅将具有特定属性的项目包含在索引中,从而减少索引大小和成本。 要深入理解 NoSQL 设计模式,专业书籍 (Amazon)会有所帮助。
设计最佳实践与常见陷阱
单表设计作为 DynamoDB 的最佳实践被广泛推荐,但并非适合所有场景。对于实体间关系复杂且访问模式频繁变化的应用,分表可能更容易运维。GSI 的写入节流会传导到主表,因此需设计 GSI 容量以跟上主表的写入量。使用 BatchWriteItem 写入超过 25 项时,必须实现未处理项目的重试逻辑。在 DynamoDB Streams 与 Lambda 结合的事件驱动模式中,需注意 Lambda 的并行执行取决于分区数量。Adaptive Capacity 会自动提升热分区的吞吐量,但无法超过表的总预置容量。
与 RDS 的选型对比
DynamoDB 专长于基于键的读写,在简单的键查找和范围查询中提供极低延迟。而复杂的 JOIN 查询、聚合查询以及以 ACID 事务为中心的工作负载更适合 RDS(Aurora / PostgreSQL)。DynamoDB 事务(TransactWriteItems / TransactGetItems)限制为最多 100 个项目,不支持跨多表的复杂事务。用户画像、会话存储、实时排行榜、IoT 设备数据采集等场景最适合 DynamoDB。以报表和分析查询为主的工作负载,从 DynamoDB 导出到 S3 后用 Athena 分析,或通过 DynamoDB Streams 同步到 OpenSearch 更为高效。
DynamoDB 费用优化
DynamoDB 按需模式按读取(每 100 万 RRU 约 0.25 美元)和写入(每 100 万 WRU 约 1.25 美元)计费。预置模式按 WCU/RCU 的小时计费,对于稳定的工作负载,结合 Auto Scaling 可提高成本效率。预留容量最高可享受 77% 的折扣。GSI 消耗独立的容量,因此应删除不必要的 GSI,并将投影属性最小化以降低存储成本。通过 TTL 自动删除过期数据来控制存储成本。
总结
DynamoDB 是通过分区键和排序键设计实现毫秒级延迟的 NoSQL 数据库。通过 GSI 重载和单表设计支持多种访问模式,利用稀疏索引优化索引成本。通过按需模式和预置模式的合理选择以及 TTL 自动删除数据,实现高效的成本管理。