DynamoDB Partition Key 如何工作
你的 partition key 不是一个列——它是一个地址。DynamoDB 哈希那个 key,哈希结果决定哪台 物理机器存这个 item。key 挑得好,负载就铺开;挑得糟,一台服务器就扛下所有的火力。
DynamoDB 分区键是如何工作的?
DynamoDB 将你的分区键(partition key)送入内部哈希函数,该哈希结果决定哪个物理分区存储此项目(item)。这个键既不像 SQL 列那样被排序,也不会被索引——它是一个地址。选择高基数(high-cardinality)的键,负载就会分散到多个分区;选择低基数的键,单个分区就会承受所有压力。
- key 是被哈希的,不是被排序的。 DynamoDB 把你的 partition key 跑过一个内部哈希来挑 分区。两个相邻的值在磁盘上落得八竿子打不着。
- 一个分区是一个真实的存储单元。 每个大约封顶在 10 GB、3,000 读取单元/秒、1,000 写入单元/秒。你的流量被你的 key 铺开到多少个分区上一除,就是每个分区分到的。
- 热 key 是自坑。 把大多数请求漏斗进一个 partition key 值,你就在那个分区上被限流, 而表的其余部分闲坐着。
- 高基数的 key 赢。 你拥有的、被均匀命中的不同 key 值越多,吸收负载的分区就越多。
从 key 实际干什么开始
从 SQL 过来,主键是一个被排序、被索引的列,你在它上面 JOIN 和 ORDER BY。在 DynamoDB
里,partition key(有时叫 hash key)做的事不同:它决定 放置。
DynamoDB 把 partition key 喂进一个内部哈希函数。输出映射到一个 keyspace,而 keyspace 被 切成一段段——每一段由一个 物理分区 拥有。那个分区是真实节点上的真实存储。
所以 partition key 回答一个问题:哪台机器存这个 item? sort key,如果你有的话,只在 那台机器 内部 给 item 排序。它在放置上不起任何作用。
跟着一次写入穿过哈希
假设你跑一个摄取设备读数的 SaaS。你的表 SensorReadings 用一个 partition key deviceId
和一个 sort key readingTs。你为 deviceId = "vac-7741" 写一条读数。
这就是那次写入走的路径——从你的 key 到它落地的磁盘:
vac-7741 的写入被哈希到 keyspace 中的一点,那点落在 P2 的范围里,于是 item 落在 P2 上——
在那里按 readingTs 排序。
要内化的东西:"vac-7741" 和 "vac-7742" 只差一个字符,但它们的哈希毫不相关。它们几乎
肯定住在不同的分区上。在 partition keyspace 里没有"邻近"这回事。
这就是 DynamoDB 从最初设计继承来的一致性哈希思想——2007 年的 Amazon Dynamo 论文 ("Dynamo: Amazon's Highly Available Key-value Store")正是靠哈希把 key 散布到各节点, 好让没有单个节点变成瓶颈。
尊重分区的硬限制
一个物理分区是有限的。按 AWS DynamoDB 开发者指南,每个大约最多容纳:
| 限制 | 每分区 |
|---|---|
| 存储 | ~10 GB |
| 读取吞吐量 | 3,000 读取单元/秒 |
| 写入吞吐量 | 1,000 写入单元/秒 |
当一个分区填过 10 GB,或你预置的吞吐量需要更多空间时,DynamoDB 拆分 它——keyspace 范围 被分开,item 重新分布到更多分区上。这是自动的;你不会触发它。
要害是:一次拆分按 item 的 哈希 来铺开它们。如果每个热门请求都瞄准单个 partition key 值, 拆分帮不上忙——所有那些流量仍然哈希到同一个点。你没法把单个 key 的负载拆到多个分区上。
点名陷阱:热分区
热分区 是经典自坑。它发生在一个 partition key 值(或一小撮值)吸收了不成比例的流量份额 时。
具体的翻车:你把 SensorReadings 切换到一个 partition key region,值像 "us-east"、
"eu-west"。三个区域意味着三个 key 值,意味着——至多——三个分区在干真活。猛灌 "us-east"
读取,它就在 3,000 RCU 限流,而表的总预置容量闲置着。
DynamoDB 的 自适应容量 缓和这一点——它能把闲置吞吐量挪向一个繁忙分区,并把单个非常热的 key 隔离 到它自己的分区上。AWS 在 re:Invent "Advanced Design Patterns for DynamoDB" 深入会议里详述了这个。但自适应容量买来的是时间,不是豁免:它没法细分单个 key 的负载。为 铺开而设计;别靠那张安全网。
选一个高基数的 key
修法是 基数——不同 key 值的数量,以及流量命中它们有多均匀。
- 低基数(
region、status、true/false):分区少,流量集中,你早早就限流。 - 高基数(
deviceId、userId、一个订单 ID):许多值哈希到许多分区,负载铺开,余量增长。
从 SQL 过来你会乐呵呵地给一个 status 列建索引并在上面过滤。作为 DynamoDB 的 partition
key 那是个陷阱——它没法铺开。把低基数属性留作过滤条件,或留作一个
二级索引的 sort key,绝不要作为决定放置的那个东西。
当一个本来不错的 key 仍然偏斜时——一小撮鲸鱼租户盖过其余——加一个后缀,把一个逻辑值
扇到 N 个分区,例如分片写入路径用 tenantId#3。读取时你再聚合回来。
要在你的 key 铺开之后瞄准一个分区 内部 的 item,你会在 sort key 上写一个
KeyConditionExpression。你可以在
DynamoDB 表达式构建器里针对你自己的 schema 把它搭好,
再接进代码:
deviceId = "vac-7741" AND readingTs BETWEEN "2026-06-01" AND "2026-06-30"
那从单个分区读出一台设备的六月窗口——是一个 Query,不是一个
Scan。partition key 钉住机器;sort key 条件收窄行。
坑与下一步
- 别按在 SQL 里读着顺的来挑 key。 按什么能 铺开 来挑。基数第一,查询方便第二。
- 别以为表的总容量是你每个 key 都能用的。 吞吐量是按分区的;一个热值可以在表看起来闲着 的时候被限流。
- 别跟拆分较劲。 它是自动且哈希驱动的——你的活儿是给它足够多的不同 key 去铺开。
一旦你的 key 干净地铺开,接下来的决定是如何在一个分区内部布置 item——见 单表设计——以及一个 二级索引什么时候是第二个访问模式的对的工具。
下载 DynoTable,浏览你真实的分区和 key 分布,并在一个热 key 把你呼起来之前发现 它。