Python(boto3)中的 DynamoDB GetItem
get_item 按完整主键取回一个项目。boto3 的低层 client(boto3.client("dynamodb"))双向都说 DynamoDB JSON,所以键要带着类型包装着传进去,项目也以同样的形式回来。它和 query、scan 的区别在基于项目的操作里有讲。
代码
import boto3
client = boto3.client("dynamodb")
response = client.get_item(
TableName="Music",
Key={"Artist": {"S": "Arturo Sandoval"}, "SongTitle": {"S": "Cubano Chant"}},
ProjectionExpression="#proj0, #proj1, #proj2, #proj3",
ExpressionAttributeNames={"#proj0": "Artist", "#proj1": "SongTitle", "#proj2": "AlbumTitle", "#proj3": "Year"},
)
item = response.get("Item")
if item is None:
print("Item not found")
else:
print(item)说明
没命中时,返回的响应里压根没有 Item 这个键。不是 None,也不是空字典。在同一张表上读一个不存在的键,响应的顶层键正好是:
['ResponseMetadata']这就是代码片段用 response.get("Item") 的原因。在普通的「没找到」路径上,response["Item"] 会抛 KeyError,而这正是一行缺失的记录在 Web 处理函数里变成 500 的过程。你照样要为这次读取付费:AWS 的读容量页面写着,"if you perform a read operation on an item that doesn't exist, DynamoDB will still consume read throughput as outlined above"(2026-07-28 取回)。
Year 是保留字,这就是生成的代码片段给每个投影属性都起别名的原因。去掉那些 #proj 别名、直接传 ProjectionExpression="Year",引擎就会拒绝这次读取:
ValidationException: Invalid ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: Year无条件起别名不花任何代价,却能消掉一整类失败。完整清单有 573 个词;见 "Attribute name is a reserved keyword"。
把 Key 写错的四种方式,三种不同的消息。它们值得分清楚,因为其中没有一个是大家预期的「provided key element does not match the schema」错误。在一张以 Artist(分区键)+ SongTitle(排序键)为键的 Music 表上复现:
| 你传了什么 | 原样的 ValidationException 消息 |
|---|---|
{"Artist": …}——缺排序键 | The number of conditions on the keys is invalid |
{"Artist": …, "SongTitle": …, "Extra": …} | The number of conditions on the keys is invalid |
{"Artist": …, "Song": …}——属性名写错 | One of the required keys was not given a value |
{"Artist": {"N": "1"}, …}——类型写错 | One or more parameter values were invalid: Type mismatch for key |
注意缺少一个键属性和多出一个键属性给出的是同一条消息,所以「number of conditions」的意思是「你交给我的不是恰好那套键模式」,而不是「你传得太少了」。
ProjectionExpression 削减的是载荷,不是账单。用 ReturnConsumedCapacity="TOTAL" 以三种方式读同一个约 15 KB 的项目:
full item, eventually consistent CapacityUnits: 2.0
ProjectionExpression="#y" (Year only) CapacityUnits: 2.0
ProjectionExpression="#y" + ConsistentRead CapacityUnits: 4.0投影把响应从约 15 KB 变成了一个数字,而成本一点没变。AWS 说得很直白:"The number of capacity units consumed will be the same whether you request all of the attributes (the default behavior) or just some of them (using a projection expression)"(Query API 参考文档,2026-07-28 取回)。ConsistentRead=True 是那张清单上唯一能动这个数字的开关,而且它把数字翻了一倍。投影到底是干什么用的,见投影表达式。
资源 API 是另一份契约,不是同一件事的漂亮写法。boto3.resource("dynamodb").Table("Music").get_item(...) 返回的是普通 Python,每个数字都是 decimal.Decimal:
{'Artist': 'Arturo Sandoval', 'AlbumTitle': 'Danzon', 'Awards': Decimal('0'), 'Year': Decimal('1994'), 'SongTitle': 'Cubano Chant'}这是把双刃剑。用同一套 API 拿 float 写回去,请求还没离开你的机器就会报错:
TypeError: Float types are not supported. Use Decimal types instead.要是被这个咬到了,"Float types are not supported" 里有修法。真正的陷阱是在一个代码库里混用这两套 API:低层 client 会欣然接受 {"N": "1.5"},而资源 API 本来会拒绝它。
错误以 botocore 异常的形式到达,而 boto3 给了它们真正的类。在 1.43.58 上,条件失败时抛出的对象是 ConditionalCheckFailedException,它是 ClientError 的子类,所以 except ClientError 加上 err.response["Error"]["Code"] 判断,和 except client.exceptions.ConditionalCheckFailedException 两种写法都能用。用你代码库里已有的那种;不要拿 str(e) 去匹配。
用可视化的方式来做
在你动手起别名之前:免费的 DynamoDB 保留字检查器接收你的属性名,告诉你撞上了 573 个保留字中的哪些,并给出可直接粘贴的 ExpressionAttributeNames 映射。
要浏览表并针对你自己的数据运行 GetItem——键的表单、结果网格、把请求复制成 boto3 代码——请下载 DynoTable。
相关指南
- Query 与 Scan 的对比——什么时候一次
get_item胜过query。 - DynamoDB 数据类型——每种属性类型在 DynamoDB JSON 里怎么表示。
- DynamoDB ResourceNotFoundException——这里通常遇到的第一个错误:表名或区域写错了。
- "The provided key element does not match the schema"——你传的键与表的键模式不匹配。
参考资料
- GetItem — Amazon DynamoDB API Reference
- get_item — Boto3 DynamoDB.Client Reference
- Read consistency — Amazon DynamoDB Developer Guide
- Capacity unit consumption — Amazon DynamoDB Developer Guide
2026-07-28 针对 DynamoDB Local(amazon/dynamodb-local,端口 9000)以 boto3 1.43.58 / botocore 1.43.58 复现。上方每一条消息和每一个容量数字都是引擎输出,原样照录。DynamoDB Local 不是线上服务;已知两者措辞不同的地方,我们会在对应的错误页上说明。