DynamoDB写入数据 #
一、PutItem概述 #
1.1 基本概念 #
PutItem 用于向表中写入一个新项目,或替换现有项目。
text
PutItem特点:
├── 创建新项目
├── 完全替换现有项目
├── 支持条件写入
├── 支持返回旧值
└── 原子操作
1.2 与UpdateItem区别 #
| 操作 | 行为 | 适用场景 |
|---|---|---|
| PutItem | 完全替换项目 | 创建或完全替换 |
| UpdateItem | 部分更新属性 | 修改部分属性 |
二、基本写入 #
2.1 使用CLI #
bash
aws dynamodb put-item \
--table-name Users \
--item '{
"UserId": {"S": "user123"},
"Name": {"S": "John Doe"},
"Email": {"S": "john@example.com"},
"Age": {"N": "30"},
"IsActive": {"BOOL": true}
}'
2.2 使用JavaScript SDK #
javascript
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, PutCommand } = require('@aws-sdk/lib-dynamodb');
const client = new DynamoDBClient({ region: 'us-east-1' });
const docClient = DynamoDBDocumentClient.from(client);
await docClient.send(new PutCommand({
TableName: 'Users',
Item: {
UserId: 'user123',
Name: 'John Doe',
Email: 'john@example.com',
Age: 30,
IsActive: true
}
}));
2.3 使用Python SDK #
python
import boto3
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Users')
table.put_item(
Item={
'UserId': 'user123',
'Name': 'John Doe',
'Email': 'john@example.com',
'Age': 30,
'IsActive': True
}
)
2.4 使用Java SDK #
java
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
DynamoDbClient client = DynamoDbClient.create();
Map<String, AttributeValue> item = new HashMap<>();
item.put("UserId", AttributeValue.builder().s("user123").build());
item.put("Name", AttributeValue.builder().s("John Doe").build());
item.put("Email", AttributeValue.builder().s("john@example.com").build());
item.put("Age", AttributeValue.builder().n("30").build());
item.put("IsActive", AttributeValue.builder().bool(true).build());
PutItemRequest request = PutItemRequest.builder()
.tableName("Users")
.item(item)
.build();
client.putItem(request);
三、条件写入 #
3.1 仅当不存在时写入 #
bash
aws dynamodb put-item \
--table-name Users \
--item '{
"UserId": {"S": "user123"},
"Name": {"S": "John Doe"}
}' \
--condition-expression 'attribute_not_exists(UserId)'
javascript
await docClient.send(new PutCommand({
TableName: 'Users',
Item: {
UserId: 'user123',
Name: 'John Doe'
},
ConditionExpression: 'attribute_not_exists(UserId)'
}));
3.2 条件检查属性值 #
javascript
await docClient.send(new PutCommand({
TableName: 'Users',
Item: {
UserId: 'user123',
Name: 'John Doe',
Version: 1
},
ConditionExpression: 'attribute_not_exists(Version) OR Version < :maxVersion',
ExpressionAttributeValues: {
':maxVersion': 10
}
}));
3.3 常用条件表达式 #
javascript
// 属性不存在
'attribute_not_exists(UserId)'
// 属性存在
'attribute_exists(Email)'
// 属性值比较
'Age > :minAge'
'Status = :status'
// 属性类型检查
'attribute_type(Email, :type)' // :type = "S"
// 字符串操作
'begins_with(Email, :prefix)'
'contains(Tags, :tag)'
// 大小检查
'size(Description) < :maxSize'
// 组合条件
'attribute_not_exists(UserId) AND Email = :email'
3.4 处理条件失败 #
javascript
try {
await docClient.send(new PutCommand({
TableName: 'Users',
Item: { UserId: 'user123', Name: 'John Doe' },
ConditionExpression: 'attribute_not_exists(UserId)'
}));
console.log('Item created successfully');
} catch (error) {
if (error.name === 'ConditionalCheckFailedException') {
console.log('Item already exists');
} else {
throw error;
}
}
四、返回值配置 #
4.1 返回旧值 #
javascript
const response = await docClient.send(new PutCommand({
TableName: 'Users',
Item: {
UserId: 'user123',
Name: 'John Smith', // 更新名字
Email: 'john@example.com'
},
ReturnValues: 'ALL_OLD' // 返回被替换的旧项目
}));
if (response.Attributes) {
console.log('Old item:', response.Attributes);
}
4.2 返回值选项 #
| 选项 | 说明 |
|---|---|
| NONE | 不返回任何值(默认) |
| ALL_OLD | 返回被替换的旧项目 |
| UPDATED_OLD | 不适用于PutItem |
五、写入复杂类型 #
5.1 写入嵌套对象 #
javascript
await docClient.send(new PutCommand({
TableName: 'Users',
Item: {
UserId: 'user123',
Name: 'John Doe',
Address: {
Street: '123 Main St',
City: 'Beijing',
Country: 'China',
ZipCode: '100000'
},
Contacts: {
Email: 'john@example.com',
Phone: '+86-138-xxxx-xxxx'
}
}
}));
5.2 写入列表 #
javascript
await docClient.send(new PutCommand({
TableName: 'Products',
Item: {
ProductId: 'prod001',
Name: 'iPhone 15',
Tags: ['electronics', 'smartphone', 'apple'],
Prices: [999, 1099, 1199],
Specifications: [
{ Key: 'Storage', Value: '128GB' },
{ Key: 'Color', Value: 'Black' }
]
}
}));
5.3 写入集合 #
javascript
await docClient.send(new PutCommand({
TableName: 'Users',
Item: {
UserId: 'user123',
Name: 'John Doe',
Tags: new Set(['premium', 'verified', 'active']),
Scores: new Set([95, 87, 92])
}
}));
5.4 写入二进制数据 #
javascript
const fs = require('fs');
const imageBuffer = fs.readFileSync('image.png');
await docClient.send(new PutCommand({
TableName: 'Files',
Item: {
FileId: 'file001',
FileName: 'image.png',
Content: imageBuffer,
MimeType: 'image/png',
Size: imageBuffer.length
}
}));
六、性能优化 #
6.1 批量写入 #
对于大量写入,使用BatchWriteItem:
javascript
const { BatchWriteCommand } = require('@aws-sdk/lib-dynamodb');
const items = [
{ UserId: 'user1', Name: 'User One' },
{ UserId: 'user2', Name: 'User Two' },
{ UserId: 'user3', Name: 'User Three' }
];
const putRequests = items.map(item => ({
PutRequest: { Item: item }
}));
await docClient.send(new BatchWriteCommand({
RequestItems: {
Users: putRequests
}
}));
6.2 并行写入 #
javascript
const items = [
{ UserId: 'user1', Name: 'User One' },
{ UserId: 'user2', Name: 'User Two' },
{ UserId: 'user3', Name: 'User Three' }
];
await Promise.all(
items.map(item =>
docClient.send(new PutCommand({
TableName: 'Users',
Item: item
}))
)
);
6.3 写入容量规划 #
text
WCU计算:
├── 1 WCU = 1次写入(1KB)
├── 项目大小向上取整到1KB
└── 示例:
├── 写入500字节 = 1 WCU
├── 写入2.5KB = 3 WCU
└── 写入100KB = 100 WCU
七、错误处理 #
7.1 常见错误 #
javascript
try {
await docClient.send(new PutCommand({
TableName: 'Users',
Item: { UserId: 'user123', Name: 'John Doe' }
}));
} catch (error) {
switch (error.name) {
case 'ConditionalCheckFailedException':
console.error('条件检查失败');
break;
case 'ProvisionedThroughputExceededException':
console.error('超过预置吞吐量');
break;
case 'ResourceNotFoundException':
console.error('表不存在');
break;
case 'ItemCollectionSizeLimitExceededException':
console.error('项目集合大小超限');
break;
case 'ValidationException':
console.error('参数验证失败:', error.message);
break;
default:
console.error('未知错误:', error);
}
}
7.2 重试策略 #
javascript
async function putWithRetry(params, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await docClient.send(new PutCommand(params));
} catch (error) {
if (error.name === 'ProvisionedThroughputExceededException' && i < maxRetries - 1) {
await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 100));
continue;
}
throw error;
}
}
}
八、最佳实践 #
8.1 项目大小优化 #
text
优化建议:
├── 避免存储不必要的数据
├── 压缩大文本和二进制数据
├── 使用S3存储大文件
└── 项目大小控制在400KB以内
8.2 条件写入使用场景 #
text
适用场景:
├── 防止重复创建
├── 乐观锁实现
├── 唯一性约束
└── 业务规则验证
8.3 批量写入限制 #
text
BatchWriteItem限制:
├── 每批最多25个操作
├── 每批最大16MB数据
├── 每个项目最大400KB
└── 不支持条件表达式
九、完整示例 #
9.1 用户注册示例 #
javascript
async function registerUser(userData) {
const { userId, email, name, password } = userData;
try {
await docClient.send(new PutCommand({
TableName: 'Users',
Item: {
UserId: userId,
Email: email,
Name: name,
PasswordHash: hashPassword(password),
CreatedAt: new Date().toISOString(),
IsActive: true,
Version: 1
},
ConditionExpression: 'attribute_not_exists(UserId)',
ReturnValues: 'ALL_OLD'
}));
return { success: true, userId };
} catch (error) {
if (error.name === 'ConditionalCheckFailedException') {
return { success: false, error: 'User already exists' };
}
throw error;
}
}
9.2 带版本控制的写入 #
javascript
async function updateUserWithVersion(userId, updates, expectedVersion) {
try {
const response = await docClient.send(new PutCommand({
TableName: 'Users',
Item: {
UserId: userId,
...updates,
Version: expectedVersion + 1,
UpdatedAt: new Date().toISOString()
},
ConditionExpression: 'Version = :expectedVersion',
ExpressionAttributeValues: {
':expectedVersion': expectedVersion
}
}));
return { success: true };
} catch (error) {
if (error.name === 'ConditionalCheckFailedException') {
return { success: false, error: 'Version conflict' };
}
throw error;
}
}
十、总结 #
PutItem要点:
| 特性 | 说明 |
|---|---|
| 基本功能 | 创建或替换项目 |
| 条件写入 | 支持条件表达式 |
| 返回值 | 可返回旧项目 |
| 原子性 | 单个项目原子操作 |
| 大小限制 | 最大400KB |
下一步,让我们学习更新数据!
最后更新:2026-03-27