DynamoDB 复合主键
复合主键是两个属性:一个分区键和一个排序键。分区键决定一个项住在哪里;排序键给那个分区内部的各项排序。
从 SQL 过来,与其把它想成一个唯一的 id 列,不如把它想成 GROUP BY partition, ORDER BY sort 被烤进了表本身。
什么是 DynamoDB 复合主键?
DynamoDB 复合主键由两个属性组成:一个分区键和一个排序键。分区键决定一个项存放在哪个物理分区中;排序键给那个分区内部的各项排序。两者共同构成项的唯一标识,并让一次 Query 返回一个排好序的范围,而非单个项。
- 两部分,两份活。 分区键把项路由到一个物理分区;排序键给共享那个分区键的每一个项排序。
- 唯一性是这一对。 只要排序键不同,两个项就可以共享一个分区键值 —— 这就是一个分区如何容纳许多行。
- 排序键是整个要点。 是它让一次
Query返回一个范围(>=、between、begins_with)而非一个项,且不需Scan。 - 键必须是标量。 分区键和排序键只能是字符串、数字或二进制 —— 没有映射,没有列表(AWS 文档)。
简单键 vs 复合键
一个简单主键就只是一个分区键。它唯一地标识一个项,而你用 GetItem 把它读回来。仅此而已 —— 没有范围读取,没有“给我最新的 N 个”。
复合键加上排序键,而那单单一项添加,就是让 DynamoDB 感觉像个数据库、而非一张哈希表的东西。
| 简单键 | 复合键 | |
|---|---|---|
| 属性 | 仅分区键 | 分区键 + 排序键 |
| 唯一性 | 分区键值 | 这两个值组成的对 |
| 每分区多项 | 否 | 是 |
Query 一个范围 | 否(仅 GetItem) | 是(begins_with、between、>) |
| 天然适配 | 按 id 查找 | 时间序列、一对多、历史 |
给传感器读数表建模
假设你从一批现场传感器收集温度采样。访问模式是“在一个时间窗口内,取某个设备的读数,最新优先”。那是教科书式的复合键。
用设备 id 作分区键、读数时间戳作排序键:
| deviceId | readingTs | tempC | humidity |
|---|---|---|---|
| DEV#a1b2 | 2026-06-23T08:00:00Z | 21.4 | 48 |
| DEV#a1b2 | 2026-06-23T08:05:00Z | 21.7 | 47 |
| DEV#a1b2 | 2026-06-23T08:10:00Z | 22.1 | 46 |
| DEV#c9d8 | 2026-06-23T08:00:00Z | 19.8 | 55 |
全部三个 DEV#a1b2 读数都落在同一个分区,物理上存在一起,并按 readingTs 排序。
AWS 把分区键叫做 哈希属性、把排序键叫做 范围属性 —— 排序键是一个你能在其内部扫描的范围(AWS 文档)。
下面是各项如何在每个分区键之下坍缩成一个项集合:
对分区键的一次 Query 读取那个设备的每一条读数,已经按时间戳排好序 —— 客户端无需排序,无需第二次往返。
查询那个范围,别扫描它
因为 readingTs 是一个 ISO-8601 字符串,它按字典序排序的方式与按时间顺序排序的方式相同。所以一次时间窗口读取是一个键条件范围,而非一个筛选:
Query
deviceId = "DEV#a1b2"
readingTs BETWEEN "2026-06-23T08:00:00Z" AND "2026-06-23T08:10:00Z"
那是一个 KeyConditionExpression —— 它在 DynamoDB 返回数据 之前 缩小读取,所以你只为窗口内的项付费。一个 FilterExpression 在读取 之后 运行,并就它扫过的一切向你计费;那是缩小版的 Scan 暗坑。
表达式本身,连同占位符和带类型的值,手写起来很麻烦。用 DynamoDB 表达式构建器可视化地构建它,并把精确的 KeyConditionExpression 拷进你的 SDK 调用。
有意地设计排序键
排序键不是免费的元数据 —— 它是范围读取的唯一杠杆,所以把它塑造成贴合你的查询。
- 用一个可排序的时间戳。 ISO-8601 字符串或补零的纪元数字排序正确;原始的本地化日期不行。
- 给它加前缀以做一对多重载。 一个像
READING#2026-06-23T08:00:00Z的排序键让你在一个分区下混合实体类型,并用begins_with切分它们。那是通向单表设计的接缝。 - 把高基数维度放进分区键。 传感器 id 有数千个值,所以它均匀地分散写入。一个低基数分区键(比如
region)会造出一个热分区。
复合键何时咬你
它是一项承诺,而非一项便利。陷阱:你挑了一个分区键、上线了,然后发现一个需要 不同 分组的访问模式 —— “整个机队里所有高于 30°C 的读数”。
基表回答不了那个;分区键是固定的。你的选项是一个带不同键的全局二级索引,或者重构。
在你提交键模式 之前 把你的读取枚举出来。改一个主键意味着一次表迁移,而非一次 ALTER TABLE。
下一步
复合键是项集合、一对多关系和大多数有用的索引设计之下的根基 —— 接下来读单表设计和 GSI 对比 LSI,看它们通向何处。
在 DynamoDB 表达式构建器里勾勒你的 KeyConditionExpression,然后试用 DynoTable,去浏览你真实的分区,看排序顺序对着你自己的表对齐起来。