DynamoDB 项大小限制(400 KB)
单个 DynamoDB 项最多能容纳 400 KB 的数据。从 MongoDB(16 MB 的文档)或一个几乎没有实际上限的关系型行过来,这个上限感觉很低——而且你往往是以吃亏的方式发现它的:一个稳定运行了几个月的写入突然以 ValidationException 失败,因为某个项终于长得太大了。
这个限制不是随意定的,也不是你能提高的配额。它是一个建模约束,而撞上它的那些项,通常是在告诉你数据建模错了。
DynamoDB 中项的最大大小是多少?
DynamoDB 把单个项封顶在 400 KB——一个你无法提高的硬限制。这个大小把属性名和值算在一起,包括每一个嵌套的列表、映射和集合元素。项通常是通过无界增长撞上它的,比如一个不断膨胀的内嵌列表;修复靠的是建模——把集合拆成独立的项——而不是压缩。
- 每个项 400 KB,硬上限。不可调整,不是软配额。
- 大小 = 属性名 + 值,合在一起。长属性名要计入,且在每一个项上都要计。
- 嵌套和集合也要计。列表、映射及其嵌套的值全都累加。
- 通常的成因是无界增长——在一个父项上内嵌一个无限增长的列表。
- 修复靠的是建模,不是压缩。把那个增长的集合拆成它自己的项,放在一个共享的分区键下。
问题所在:永远增长的那个项
假设你追踪一支车队,你决定把每辆车的遥测读数存成车辆项上的一个列表:
PK: VEHICLE#A1 readings: [ {ts, lat, lng, fuel}, {ts, lat, lng, fuel}, ... ]头一两天它没问题。但读数每隔几秒钟就到来且永不停歇,所以列表无界增长。最终多来一条读数就会把项推过 400 KB,DynamoDB 以 ValidationException: Item size has exceeded the maximum allowed size 拒绝这次写入——你再也没法为那辆车记录遥测了,因为每次更新都要重写整个项。
问题不在大小限制。而在于把一个无界的一对多关系建模成了一个内嵌列表。那只有当“多”的那一侧有界且很小时才行得通。
到底什么会计入 400 KB
DynamoDB 把项的总大小衡量为以下各项之和:
- 每一个属性名,按 UTF-8 编码。一个 20 字符的名字在数百万个项上重复,既是你要付费的大小,也是存储——这就是为什么有经验的建模者把属性名保持得很短。
- 每一个属性值。字符串和二进制按字节长度算;数字按一种紧凑编码算;布尔和空值按一个微小的固定成本算。
- 嵌套结构。一个列表或映射要算它自身的开销加上它内部每一个元素和键的大小,一直算到底。
没有一个单独的每属性上限需要你去规划——是整个项对着 400 KB 这条线。AWS 的项大小文档详细写明了确切的字节记账方式。
这个限制为什么存在
大的项搬起来很贵。DynamoDB 的读取以 4 KB 为单位计量,所以一个 400 KB 的项做强一致读取要花 100 RCU——而且随着项变大,读取、写入和复制都变得更慢更贵。这个上限把你推向小而有针对性的项,远离那种“取回一个巨大 blob”的反模式——NoSQL 新手出于关系型习惯常会伸手去用那种做法。
在建模上规避它
对于车队这个例子,别再内嵌了。给每一条读数它自己的项,放在与车辆相同的分区里,按排序键上的时间戳排序:
PK: VEHICLE#A1 SK: READING#2026-06-27T10:00:05Z lat, lng, fuel
PK: VEHICLE#A1 SK: READING#2026-06-27T10:00:10Z lat, lng, fuel现在没有哪个单独的项会增长,写入永远长不出上限,而对 VEHICLE#A1 的单次 Query 仍然把某辆车的读数作为一个有序的项集合拉回来。有界的子列表(少量几个标签、一个固定的配置块)内嵌是没问题的;无界的那些则要变成项。
在 DynoTable 里检查项大小
在你敲定一种形态之前,先掂量一个有代表性的项。在 DynoTable 里,用 Quick View 打开一个项,它会在属性旁边显示项的字节大小——这样你在浏览真实数据时就能抓到一个过重的形态,在设计时抓到,而不是在写入失败时。
更愿意留在浏览器里?DynamoDB 项大小计算器从一个粘贴的样本做同样的事,报告确切的 KB 数,以及每次读取和写入将花费的 RCU/WCU。

已对着 DynamoDB 核实过
大小规则说起来容易,写错了也不容易被察觉,所以我们拿自己的算法去对了唯一算数的那个权威:DynamoDB 实际怎么计费。
窍门在于 ConsumedCapacity 报告的是单元数而不是字节数,而写入 N 字节要花 ceil(N / 1024) WCU。一般情况下这很粗,但在边界上它精确得很。一个刚好落在 1024 字节的项必须花 1 WCU,一个刚好落在 1025 字节的必须花 2 WCU——所以算术上哪怕差一个字节,观察到的数字就会翻面。
| 项 | 我们算出的字节数 | 预测 WCU | 实际 WCU |
|---|---|---|---|
| 正好 1 KB | 1,024 | 1 | 1 |
| 1 KB + 1 字节 | 1,025 | 2 | 2 |
| 正好 2 KB | 2,048 | 2 | 2 |
| 2 KB + 1 字节 | 2,049 | 3 | 3 |
| 正好 4 KB | 4,096 | 4 | 4 |
| 混合类型 | 32 | 1 | 1 |
六项全中,而真正构成证明的是那两组边界:1,024 字节和 1,025 字节的项只差一个填充字符,DynamoDB 对它们的收费却不同,恰好落在我们的计算所说的那一级台阶上。
实际后果就是那个会花钱的后果。一个只比 1 KB 多出一个字节的项,要多花整整一个 WCU,而在 4 KB 处,同样的悬崖出现在读取侧——所以把一个项从 1,025 字节修到 1,024 字节,就把它的写入成本砍掉一半;而把它从 1,024 撑到 1,025,就让成本翻倍。属性名也计入总数,这就是为什么在一张热表上缩短键名不是微优化。
陷阱与后续步骤
- 盯着随流量增长的内嵌列表——它们是经典的 400 KB 定时炸弹。给它们设定边界,或把它们拆出去。
- 缩短属性名,在高基数的项上——这是白拿回来的大小和存储。
- 大的值属于 S3。把大的 blob(图片、文档)存在 S3,项上只留键。
- 相关:反规范化和一对多关系讲了何时内嵌、何时拆分。
想一眼看遍一张表里各项的真实大小?下载 DynoTable,直接检视你的数据。
容量数字于 2026-07-26 在 us-east-1 针对实时 DynamoDB 服务核实(pnpm content:verify-item-size)。


