DynamoDB删除数据 #

一、DeleteItem概述 #

1.1 基本概念 #

DeleteItem 用于从表中删除单个项目。

text
DeleteItem特点:
├── 删除单个项目
├── 支持条件删除
├── 支持返回被删除的项目
├── 原子操作
└── 项目不存在不报错

1.2 删除方式对比 #

方式 说明 适用场景
DeleteItem 删除单个项目 精确删除
BatchWriteItem 批量删除 大量删除
TTL 自动过期删除 定时清理

二、基本删除 #

2.1 使用CLI #

简单主键:

bash
aws dynamodb delete-item \
  --table-name Users \
  --key '{"UserId": {"S": "user123"}}'

复合主键:

bash
aws dynamodb delete-item \
  --table-name Orders \
  --key '{
    "UserId": {"S": "user123"},
    "OrderId": {"S": "order001"}
  }'

2.2 使用JavaScript SDK #

javascript
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, DeleteCommand } = require('@aws-sdk/lib-dynamodb');

const client = new DynamoDBClient({ region: 'us-east-1' });
const docClient = DynamoDBDocumentClient.from(client);

// 简单主键
await docClient.send(new DeleteCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' }
}));

// 复合主键
await docClient.send(new DeleteCommand({
  TableName: 'Orders',
  Key: {
    UserId: 'user123',
    OrderId: 'order001'
  }
}));

2.3 使用Python SDK #

python
import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('Users')

# 简单主键
table.delete_item(
    Key={'UserId': 'user123'}
)

# 复合主键
table.delete_item(
    Key={
        'UserId': 'user123',
        'OrderId': 'order001'
    }
)

三、条件删除 #

3.1 仅当条件满足时删除 #

bash
aws dynamodb delete-item \
  --table-name Users \
  --key '{"UserId": {"S": "user123"}}' \
  --condition-expression 'IsActive = :active' \
  --expression-attribute-values '{":active": {"BOOL": false}}'
javascript
await docClient.send(new DeleteCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  ConditionExpression: 'IsActive = :active',
  ExpressionAttributeValues: {
    ':active': false
  }
}));

3.2 常用条件表达式 #

javascript
// 仅当状态为特定值时删除
ConditionExpression: 'Status = :status'

// 仅当版本匹配时删除
ConditionExpression: 'Version = :expectedVersion'

// 仅当属性存在时删除
ConditionExpression: 'attribute_exists(Email)'

// 仅当属性不存在时删除
ConditionExpression: 'attribute_not_exists(DeletedAt)'

// 组合条件
ConditionExpression: 'Status = :status AND CreatedAt < :cutoffDate'

// 集合包含检查
ConditionExpression: 'contains(Tags, :tag)'

3.3 条件删除示例 #

删除非活跃用户:

javascript
async function deleteInactiveUser(userId) {
  try {
    await docClient.send(new DeleteCommand({
      TableName: 'Users',
      Key: { UserId: userId },
      ConditionExpression: 'IsActive = :inactive',
      ExpressionAttributeValues: {
        ':inactive': false
      }
    }));
    return { success: true };
  } catch (error) {
    if (error.name === 'ConditionalCheckFailedException') {
      return { success: false, error: 'User is still active' };
    }
    throw error;
  }
}

带版本检查的删除:

javascript
async function deleteWithVersion(userId, expectedVersion) {
  try {
    await docClient.send(new DeleteCommand({
      TableName: 'Users',
      Key: { UserId: userId },
      ConditionExpression: 'Version = :version',
      ExpressionAttributeValues: {
        ':version': expectedVersion
      }
    }));
    return { success: true };
  } catch (error) {
    if (error.name === 'ConditionalCheckFailedException') {
      return { success: false, error: 'Version mismatch' };
    }
    throw error;
  }
}

四、返回值配置 #

4.1 返回被删除的项目 #

javascript
const response = await docClient.send(new DeleteCommand({
  TableName: 'Users',
  Key: { UserId: 'user123' },
  ReturnValues: 'ALL_OLD'
}));

if (response.Attributes) {
  console.log('Deleted item:', response.Attributes);
} else {
  console.log('Item did not exist');
}

4.2 返回值选项 #

选项 说明
NONE 不返回任何值(默认)
ALL_OLD 返回被删除的项目

4.3 实用示例 #

javascript
async function deleteUserAndArchive(userId) {
  const response = await docClient.send(new DeleteCommand({
    TableName: 'Users',
    Key: { UserId: userId },
    ReturnValues: 'ALL_OLD'
  }));
  
  if (response.Attributes) {
    // 归档删除的用户数据
    await archiveUser(response.Attributes);
    return { success: true, deletedUser: response.Attributes };
  }
  
  return { success: false, error: 'User not found' };
}

五、批量删除 #

5.1 使用BatchWriteItem #

javascript
const { BatchWriteCommand } = require('@aws-sdk/lib-dynamodb');

async function batchDeleteUsers(userIds) {
  const deleteRequests = userIds.map(id => ({
    DeleteRequest: {
      Key: { UserId: id }
    }
  }));
  
  await docClient.send(new BatchWriteCommand({
    RequestItems: {
      Users: deleteRequests
    }
  }));
}

5.2 处理未处理的项目 #

javascript
async function batchDeleteWithRetry(tableName, keys) {
  const deleteRequests = keys.map(key => ({
    DeleteRequest: { Key: key }
  }));
  
  let requestItems = { [tableName]: deleteRequests };
  
  while (Object.keys(requestItems).length > 0) {
    const response = await docClient.send(new BatchWriteCommand({
      RequestItems: requestItems
    }));
    
    if (response.UnprocessedItems && Object.keys(response.UnprocessedItems).length > 0) {
      requestItems = response.UnprocessedItems;
      await new Promise(resolve => setTimeout(resolve, 100));
    } else {
      break;
    }
  }
}

5.3 BatchWriteItem限制 #

text
限制:
├── 每批最多25个操作
├── 每批最大16MB数据
├── 不支持条件表达式
└── 同一项目不能重复

六、删除大量数据 #

6.1 分批删除 #

javascript
async function deleteAllItems(tableName, partitionKey) {
  let lastKey = null;
  
  do {
    // 查询一批数据
    const queryResponse = await docClient.send(new QueryCommand({
      TableName: tableName,
      KeyConditionExpression: 'PK = :pk',
      ExpressionAttributeValues: { ':pk': partitionKey },
      ExclusiveStartKey: lastKey,
      Limit: 25
    }));
    
    if (queryResponse.Items && queryResponse.Items.length > 0) {
      // 批量删除
      const deleteRequests = queryResponse.Items.map(item => ({
        DeleteRequest: {
          Key: { PK: item.PK, SK: item.SK }
        }
      }));
      
      await docClient.send(new BatchWriteCommand({
        RequestItems: {
          [tableName]: deleteRequests
        }
      }));
    }
    
    lastKey = queryResponse.LastEvaluatedKey;
  } while (lastKey);
}

6.2 使用Scan删除 #

javascript
async function deleteAllItemsByScan(tableName) {
  let lastKey = null;
  
  do {
    const scanResponse = await docClient.send(new ScanCommand({
      TableName: tableName,
      ExclusiveStartKey: lastKey,
      Limit: 25,
      ProjectionExpression: 'PK, SK'  // 只获取主键
    }));
    
    if (scanResponse.Items && scanResponse.Items.length > 0) {
      const deleteRequests = scanResponse.Items.map(item => ({
        DeleteRequest: {
          Key: { PK: item.PK, SK: item.SK }
        }
      }));
      
      await docClient.send(new BatchWriteCommand({
        RequestItems: {
          [tableName]: deleteRequests
        }
      }));
    }
    
    lastKey = scanResponse.LastEvaluatedKey;
  } while (lastKey);
}

七、软删除模式 #

7.1 软删除概念 #

text
软删除特点:
├── 不实际删除数据
├── 标记为已删除
├── 可恢复数据
└── 保留历史记录

7.2 软删除实现 #

javascript
async function softDeleteUser(userId) {
  await docClient.send(new UpdateCommand({
    TableName: 'Users',
    Key: { UserId: userId },
    UpdateExpression: 'SET DeletedAt = :now, IsDeleted = :deleted',
    ExpressionAttributeValues: {
      ':now': new Date().toISOString(),
      ':deleted': true
    }
  }));
}

7.3 查询时过滤软删除 #

javascript
async function getActiveUsers() {
  const response = await docClient.send(new ScanCommand({
    TableName: 'Users',
    FilterExpression: 'attribute_not_exists(IsDeleted) OR IsDeleted = :notDeleted',
    ExpressionAttributeValues: {
      ':notDeleted': false
    }
  }));
  
  return response.Items;
}

7.4 使用GSI优化软删除查询 #

javascript
// 创建表时添加GSI
{
  GlobalSecondaryIndexes: [
    {
      IndexName: 'ActiveUsersIndex',
      KeySchema: [
        { AttributeName: 'IsDeleted', KeyType: 'HASH' }
      ],
      Projection: { ProjectionType: 'ALL' }
    }
  ]
}

// 查询活跃用户
await docClient.send(new QueryCommand({
  TableName: 'Users',
  IndexName: 'ActiveUsersIndex',
  KeyConditionExpression: 'IsDeleted = :notDeleted',
  ExpressionAttributeValues: {
    ':notDeleted': false
  }
}));

八、TTL自动删除 #

8.1 启用TTL #

bash
aws dynamodb update-time-to-live \
  --table-name Sessions \
  --time-to-live-specification \
    Enabled=true,AttributeName=ExpirationTime

8.2 写入带TTL的项目 #

javascript
await docClient.send(new PutCommand({
  TableName: 'Sessions',
  Item: {
    SessionId: 'session123',
    UserId: 'user123',
    Data: { /* session data */ },
    ExpirationTime: Math.floor(Date.now() / 1000) + 3600  // 1小时后过期
  }
}));

8.3 TTL注意事项 #

text
TTL注意事项:
├── TTL属性值为Unix时间戳(秒)
├── 删除不是即时的(通常48小时内)
├── 不保证精确删除时间
├── 不消耗WCU
└── 适合会话、日志等临时数据

九、错误处理 #

9.1 常见错误 #

javascript
try {
  await docClient.send(new DeleteCommand({
    TableName: 'Users',
    Key: { UserId: 'user123' },
    ConditionExpression: 'Status = :status',
    ExpressionAttributeValues: { ':status': 'active' }
  }));
} catch (error) {
  switch (error.name) {
    case 'ConditionalCheckFailedException':
      console.error('条件检查失败,项目未删除');
      break;
    case 'ProvisionedThroughputExceededException':
      console.error('超过预置吞吐量');
      break;
    case 'ResourceNotFoundException':
      console.error('表不存在');
      break;
    case 'ValidationException':
      console.error('参数验证失败:', error.message);
      break;
    default:
      console.error('未知错误:', error);
  }
}

9.2 重试策略 #

javascript
async function deleteWithRetry(params, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await docClient.send(new DeleteCommand(params));
    } catch (error) {
      if (error.name === 'ProvisionedThroughputExceededException' && i < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, Math.pow(2, i) * 100));
        continue;
      }
      throw error;
    }
  }
}

十、最佳实践 #

10.1 删除策略选择 #

text
策略选择:
├── 硬删除
│   ├── 不需要保留数据
│   ├── 数据隐私要求
│   └── 存储成本敏感
├── 软删除
│   ├── 需要恢复数据
│   ├── 需要历史记录
│   └── 审计要求
└── TTL
    ├── 自动过期
    ├── 会话数据
    └── 日志数据

10.2 性能考虑 #

text
性能建议:
├── 使用批量删除减少请求次数
├── 合理设置删除条件
├── 避免高峰期大量删除
└── 监控删除操作

10.3 安全考虑 #

text
安全建议:
├── 使用条件删除保护数据
├── 实现软删除便于恢复
├── 记录删除操作日志
└── 定期清理软删除数据

十一、总结 #

删除操作要点:

操作 说明 适用场景
DeleteItem 删除单个项目 精确删除
BatchWriteItem 批量删除 大量删除
软删除 标记删除 需要恢复
TTL 自动过期 临时数据

下一步,让我们学习批量操作!

最后更新:2026-03-27