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