DynamoDB TTL #

一、TTL概述 #

1.1 什么是TTL #

TTL(Time To Live)是DynamoDB的自动过期删除功能,根据时间戳属性自动删除过期项目。

text
TTL特点:
├── 自动删除过期项目
├── 不消耗WCU
├── 后台异步删除
├── 适合临时数据
└── 可与Streams配合使用

1.2 TTL工作原理 #

text
工作流程:
├── 1. 项目包含TTL属性
├── 2. TTL属性值为过期时间戳(秒)
├── 3. 后台进程检查过期项目
├── 4. 过期项目被标记删除
└── 5. 实际删除在48小时内完成

二、启用TTL #

2.1 使用CLI #

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

2.2 使用JavaScript SDK #

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

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

const command = new UpdateTimeToLiveCommand({
  TableName: 'Sessions',
  TimeToLiveSpecification: {
    Enabled: true,
    AttributeName: 'ExpirationTime'
  }
});

await client.send(command);

2.3 查看TTL配置 #

bash
aws dynamodb describe-time-to-live \
  --table-name Sessions
json
{
  "TimeToLiveDescription": {
    "AttributeName": "ExpirationTime",
    "TimeToLiveStatus": "ENABLED"
  }
}

三、写入带TTL的项目 #

3.1 设置过期时间 #

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

const docClient = DynamoDBDocumentClient.from(client);

// 设置1小时后过期
const expirationTime = Math.floor(Date.now() / 1000) + 3600;

await docClient.send(new PutCommand({
  TableName: 'Sessions',
  Item: {
    SessionId: 'session123',
    UserId: 'user123',
    Data: { /* session data */ },
    CreatedAt: new Date().toISOString(),
    ExpirationTime: expirationTime  // TTL属性
  }
}));

3.2 更新过期时间 #

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

// 延长会话30分钟
const newExpirationTime = Math.floor(Date.now() / 1000) + 1800;

await docClient.send(new UpdateCommand({
  TableName: 'Sessions',
  Key: { SessionId: 'session123' },
  UpdateExpression: 'SET ExpirationTime = :exp',
  ExpressionAttributeValues: {
    ':exp': newExpirationTime
  }
}));

3.3 Python示例 #

python
import boto3
import time

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

# 设置过期时间(1小时后)
expiration_time = int(time.time()) + 3600

table.put_item(
    Item={
        'SessionId': 'session123',
        'UserId': 'user123',
        'Data': {'key': 'value'},
        'ExpirationTime': expiration_time
    }
)

四、使用场景 #

4.1 会话管理 #

javascript
// 创建会话
async function createSession(userId, sessionData) {
  const sessionId = generateSessionId();
  const expirationTime = Math.floor(Date.now() / 1000) + 86400;  // 24小时
  
  await docClient.send(new PutCommand({
    TableName: 'Sessions',
    Item: {
      SessionId: sessionId,
      UserId: userId,
      Data: sessionData,
      CreatedAt: new Date().toISOString(),
      ExpirationTime: expirationTime
    }
  }));
  
  return sessionId;
}

// 获取会话
async function getSession(sessionId) {
  const response = await docClient.send(new GetCommand({
    TableName: 'Sessions',
    Key: { SessionId: sessionId }
  }));
  
  if (!response.Item) {
    return null;
  }
  
  // 检查是否过期(TTL可能还未删除)
  if (response.Item.ExpirationTime < Math.floor(Date.now() / 1000)) {
    return null;
  }
  
  return response.Item;
}

// 刷新会话
async function refreshSession(sessionId) {
  const newExpirationTime = Math.floor(Date.now() / 1000) + 86400;
  
  await docClient.send(new UpdateCommand({
    TableName: 'Sessions',
    Key: { SessionId: sessionId },
    UpdateExpression: 'SET ExpirationTime = :exp',
    ConditionExpression: 'attribute_exists(SessionId)',
    ExpressionAttributeValues: {
      ':exp': newExpirationTime
    }
  }));
}

4.2 临时数据存储 #

javascript
// 验证码存储
async function storeVerificationCode(email, code) {
  const expirationTime = Math.floor(Date.now() / 1000) + 300;  // 5分钟
  
  await docClient.send(new PutCommand({
    TableName: 'VerificationCodes',
    Item: {
      Email: email,
      Code: code,
      CreatedAt: new Date().toISOString(),
      ExpirationTime: expirationTime
    }
  }));
}

// 验证码验证
async function verifyCode(email, code) {
  const response = await docClient.send(new GetCommand({
    TableName: 'VerificationCodes',
    Key: { Email: email }
  }));
  
  if (!response.Item || response.Item.Code !== code) {
    return false;
  }
  
  // 验证成功后删除
  await docClient.send(new DeleteCommand({
    TableName: 'VerificationCodes',
    Key: { Email: email }
  }));
  
  return true;
}

4.3 日志和事件存储 #

javascript
// 存储日志(保留30天)
async function storeLog(logData) {
  const logId = generateLogId();
  const expirationTime = Math.floor(Date.now() / 1000) + (30 * 24 * 3600);
  
  await docClient.send(new PutCommand({
    TableName: 'Logs',
    Item: {
      LogId: logId,
      Level: logData.level,
      Message: logData.message,
      Timestamp: new Date().toISOString(),
      ExpirationTime: expirationTime
    }
  }));
}

4.4 限流和防重放 #

javascript
// 请求去重(防止重放攻击)
async function checkAndStoreRequestId(requestId, ttlSeconds = 300) {
  const expirationTime = Math.floor(Date.now() / 1000) + ttlSeconds;
  
  try {
    await docClient.send(new PutCommand({
      TableName: 'RequestIds',
      Item: {
        RequestId: requestId,
        ExpirationTime: expirationTime
      },
      ConditionExpression: 'attribute_not_exists(RequestId)'
    }));
    return true;  // 请求ID可用
  } catch (error) {
    if (error.name === 'ConditionalCheckFailedException') {
      return false;  // 请求ID已存在
    }
    throw error;
  }
}

五、TTL与Streams #

5.1 捕获TTL删除事件 #

javascript
// Lambda函数处理TTL删除
exports.handler = async (event) => {
  for (const record of event.Records) {
    if (record.eventName === 'REMOVE') {
      // 检查是否是TTL删除
      const userIdentity = record.userIdentity;
      const isTTLDelete = userIdentity && 
        userIdentity.type === 'Service' && 
        userIdentity.principalId === 'dynamodb.amazonaws.com';
      
      if (isTTLDelete) {
        console.log('TTL deletion:', record.dynamodb.OldImage);
        await handleTTLDelete(record.dynamodb.OldImage);
      }
    }
  }
};

async function handleTTLDelete(oldImage) {
  // 处理TTL删除逻辑
  // 如:清理关联数据、发送通知等
}

5.2 级联删除 #

javascript
// TTL删除时清理关联数据
exports.handler = async (event) => {
  for (const record of event.Records) {
    if (record.eventName === 'REMOVE') {
      const oldImage = unmarshall(record.dynamodb.OldImage);
      
      // 删除关联数据
      if (oldImage.Type === 'User') {
        await deleteUserRelatedData(oldImage.UserId);
      }
    }
  }
};

async function deleteUserRelatedData(userId) {
  // 删除用户相关的其他数据
  // ...
}

六、TTL注意事项 #

6.1 时间戳要求 #

text
时间戳要求:
├── 必须是Unix时间戳(秒)
├── 必须是数字类型
├── 不能是字符串
└── 示例:1704067200(2024-01-01 00:00:00 UTC)

6.2 删除延迟 #

text
删除延迟:
├── TTL删除不是即时的
├── 通常在48小时内完成
├── 不保证精确删除时间
├── 高负载时可能更长
└── 不应依赖精确删除时间

6.3 过期数据访问 #

javascript
// 过期数据可能仍然可访问
async function getValidSession(sessionId) {
  const response = await docClient.send(new GetCommand({
    TableName: 'Sessions',
    Key: { SessionId: sessionId }
  }));
  
  if (!response.Item) {
    return null;
  }
  
  // 手动检查过期
  const now = Math.floor(Date.now() / 1000);
  if (response.Item.ExpirationTime < now) {
    // 数据已过期但尚未被TTL删除
    return null;
  }
  
  return response.Item;
}

七、TTL最佳实践 #

7.1 时间戳计算 #

javascript
// 正确的时间戳计算
function getExpirationTime(secondsFromNow) {
  return Math.floor(Date.now() / 1000) + secondsFromNow;
}

// 常用时间常量
const TTL = {
  MINUTE: 60,
  HOUR: 3600,
  DAY: 86400,
  WEEK: 604800,
  MONTH: 2592000
};

// 使用示例
const expirationTime = getExpirationTime(TTL.HOUR);  // 1小时后

7.2 属性命名 #

text
命名建议:
├── 使用描述性名称
├── 常见名称:
│   ├── ExpirationTime
│   ├── ExpiresAt
│   ├── TTL
│   └── ValidUntil
└── 保持一致性

7.3 监控TTL #

javascript
// 监控即将过期的项目
async function getExpiringItems(tableName, minutesThreshold = 30) {
  const threshold = Math.floor(Date.now() / 1000) + (minutesThreshold * 60);
  
  const response = await docClient.send(new ScanCommand({
    TableName: tableName,
    FilterExpression: 'ExpirationTime < :threshold',
    ExpressionAttributeValues: {
      ':threshold': threshold
    }
  }));
  
  return response.Items;
}

八、禁用TTL #

bash
aws dynamodb update-time-to-live \
  --table-name Sessions \
  --time-to-live-specification \
    Enabled=false,AttributeName=ExpirationTime
javascript
const command = new UpdateTimeToLiveCommand({
  TableName: 'Sessions',
  TimeToLiveSpecification: {
    Enabled: false,
    AttributeName: 'ExpirationTime'
  }
});

九、总结 #

TTL要点:

特性 说明
功能 自动删除过期项目
属性类型 数字(Unix时间戳)
删除延迟 48小时内
成本 不消耗WCU
Streams 可捕获删除事件

下一步,让我们学习全局表!

最后更新:2026-03-27