5. AWS DynamoDB实战之Time to Live (TTL)
简介
- Dynamodb提供了一种TTL(Time To Live)机制,用于指定数据的过期时间
- 过期时间的格式为: Unix epoch time format in seconds
- Dynamodb的每个分区都会有两个后台进程来实现TTL,一个用于对表的分区进行扫描,以判断数据是否过期,另一个对过期数据进行删除.
- 从表中删除项目时,会同时执行两项后台操作:
- 按照与 DeleteItem 操作相同的方式,从任何本地二级索引和全局二级索引中删除项目。
- 每个项目的删除操作都会进入 DynamoDB Streams,但会被标记为系统删除,而不是常规删除。
- 根据表的大小和活动性级别,过期项目的实际删除操作可能会有所不同, TTL 通常在过期 48 小时内删除已过期的项目。
- 已过期但尚未被 TTL 删除的项目仍会出现在读取、查询和扫描中。如果不希望结果集中包含过期项目,可以通过filter expression将它们筛选出来(ttl > now)
应用场景
- 适用于需要在一段时间后自动删除的数据, 比如session data
用例分析
- 在 2. AWS DynamoDB实战之表的设计_meiyubaihe的专栏-CSDN博客 一文中,我们以收藏功能为例讲述了如何设计表.
- 收藏功能的基本操作就是收藏,添加标签,取消收藏.进行了取消收藏操作后,那这条数据上的标签也会删除.如果是用户误操作, 那也只能自己重新收藏并添加标签.
- 这里为了防止用户误操作,我们可以提供一个撤回操作,用于撤回取消收藏操作.结合前端的呈现,我们可以在用户取消收藏之后弹出一个提示框提示用户取消成功,同时在提示框中加一个撤回的按钮,这样用户如果在看到提示框之后,如果发现时误操作,就可以通过提示框的车回按钮进行撤回操作,撤回之后,数据会再次被收藏,并且会保留之前所有的tag.一般提示框都是展示一段时间后就会消失,所以我们在提示框消失之前需要保留数据,提示框消失之后就可以将数据删除.
- 综上,需要在FavoriteDataDto以及FavoriteDataTagDto的响应数据上设置TTL,这里我们就设置为当前时间60秒后.
实现
- 配置表: 设置表的ttl属性名,以及ttl的enable状态
- 在Dto中添加ttl属性字段
- 设置item的ttl
- 根据需求查询操作时只返回未过期的时间
配置表
表的ttl配置通过TimeToLiveSpecification指定,只包含两个配置:AttributeName和Enabled状态.
TimeToLiveSpecification:
AttributeName: ttl
Enabled: true
service: jessica-favorite-dtoprovider:name: awsstage: ${opt:stage, 'develop'}region: ${opt:region, 'ap-southeast-1'}stackName: ${self:provider.stage}-${self:service}resources:Resources:FavoriteTable:Type: AWS::DynamoDB::TableProperties:TableName: ${self:provider.stage}.FavoriteAttributeDefinitions:- AttributeName: pkAttributeType: S- AttributeName: skAttributeType: S- AttributeName: gsiOnePkAttributeType: S- AttributeName: gsiOneSkAttributeType: S- AttributeName: gsiTwoPkAttributeType: S- AttributeName: gsiTwoSkAttributeType: S- AttributeName: gsiThreePkAttributeType: S- AttributeName: gsiThreeSkAttributeType: S- AttributeName: lsiOneSkAttributeType: SKeySchema:- AttributeName: pkKeyType: HASH- AttributeName: skKeyType: RANGEProvisionedThroughput:ReadCapacityUnits: 1WriteCapacityUnits: 1BillingMode: PROVISIONEDLocalSecondaryIndexes:- IndexName: lsiOneKeySchema:- AttributeName: pkKeyType: HASH- AttributeName: lsiOneSkKeyType: RANGEProjection:ProjectionType: ALLGlobalSecondaryIndexes:- IndexName: gsiOneKeySchema:- AttributeName: gsiOnePkKeyType: HASH- AttributeName: gsiOneSkKeyType: RANGEProjection:ProjectionType: ALLProvisionedThroughput:ReadCapacityUnits: 1WriteCapacityUnits: 1- IndexName: gsiTwoKeySchema:- AttributeName: gsiTwoPkKeyType: HASH- AttributeName: gsiTwoSkKeyType: RANGEProjection:ProjectionType: ALLProvisionedThroughput:ReadCapacityUnits: 1WriteCapacityUnits: 1- IndexName: gsiThreeKeySchema:- AttributeName: gsiThreePkKeyType: HASH- AttributeName: gsiThreeSkKeyType: RANGEProjection:ProjectionType: ALLProvisionedThroughput:ReadCapacityUnits: 1WriteCapacityUnits: 1TimeToLiveSpecification: AttributeName: ttlEnabled: truePointInTimeRecoverySpecification:PointInTimeRecoveryEnabled: falseSSESpecification:SSEEnabled: falseContributorInsightsSpecification:Enabled: falseTags:- Key: productValue: ${self:service}
在Dto中添加ttl属性字段
需要注意的是属性字段名字必须与表的部署文件中配置的TimeToLiveSpecification的AttributeName的值保持一致.
package com.jessica.dynamodb.favorite.dto;import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIgnore;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBIndexRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConvertedEnum;
import com.jessica.dynamodb.constant.DynamoDBConstant;
import com.jessica.dynamodb.utils.KeyGenerator;import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@DynamoDBTable(tableName = "develop.Favorite")
@EqualsAndHashCode(callSuper = false)
public class FavoriteDataTagDto extends AbstractDto {private static final String DTO_NAME = "FavoriteDataTag";@DynamoDBIgnoreprivate String userId;@DynamoDBIgnoreprivate String dataId;@DynamoDBIgnoreprivate String tagId;@DynamoDBAttributeprivate String clipTime;@DynamoDBTypeConvertedEnum@DynamoDBAttributeprivate FavoriteDataType dataType;@DynamoDBAttributeprivate Long ttl;@DynamoDBHashKey@Overridepublic String getPk() {return KeyGenerator.createHashKey(DTO_NAME, userId, dataId);}@Overridepublic void setPk(String hashKey) {String[] keys = KeyGenerator.parseHashKey(DTO_NAME, hashKey);this.userId = keys[0];this.dataId = keys[1];}@DynamoDBRangeKey@Overridepublic String getSk() {return KeyGenerator.createRangeKey(tagId);}@Overridepublic void setSk(String rangeKey) {String[] keys = KeyGenerator.parseRangeKey(rangeKey);this.tagId = keys[0];}@DynamoDBIndexHashKey(globalSecondaryIndexName = DynamoDBConstant.GSI_ONE_NAME)public String getGsiOnePk() {return KeyGenerator.createHashKey(DTO_NAME, userId, tagId);}public void setGsiOnePk(String hashKey) {}@DynamoDBIndexRangeKey(globalSecondaryIndexName = DynamoDBConstant.GSI_ONE_NAME)public String getGsiOneSk() {return KeyGenerator.createRangeKey(clipTime, dataId);}public void setGsiOneSk(String rangeKey) {}@DynamoDBIndexHashKey(globalSecondaryIndexName = DynamoDBConstant.GSI_TWO_NAME)public String getGsiTwoPk() {// dataType will not set when query main table or gsi onereturn KeyGenerator.createHashKey(DTO_NAME, userId, tagId, dataType == null ? null : dataType.getValue());}public void setGsiTwoPk(String hashKey) {}@DynamoDBIndexRangeKey(globalSecondaryIndexName = DynamoDBConstant.GSI_TWO_NAME)public String getGsiTwoSk() {return KeyGenerator.createRangeKey(clipTime, dataId);}public void setGsiTwoSk(String rangeKey) {}
}
设置ttl
package com.jessica.dynamodb.favorite.service.impl;import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;import com.jessica.dynamodb.favorite.dao.FavoriteDataDao;
import com.jessica.dynamodb.favorite.dao.FavoriteDataHistoryDao;
import com.jessica.dynamodb.favorite.dao.FavoriteDataTagDao;
import com.jessica.dynamodb.favorite.dao.impl.FavoriteDataDaoImpl;
import com.jessica.dynamodb.favorite.dao.impl.FavoriteDataHistoryDaoImpl;
import com.jessica.dynamodb.favorite.dao.impl.FavoriteDataTagDaoImpl;
import com.jessica.dynamodb.favorite.dto.FavoriteDataDto;
import com.jessica.dynamodb.favorite.dto.FavoriteDataHistoryDto;
import com.jessica.dynamodb.favorite.dto.FavoriteDataTagDto;
import com.jessica.dynamodb.favorite.service.FavoriteService;public class FavoriteServiceImpl implements FavoriteService {private FavoriteDataHistoryDao favoriteDataHistoryDao = new FavoriteDataHistoryDaoImpl();private FavoriteDataDao favoriteDataDao = new FavoriteDataDaoImpl();private FavoriteDataTagDao dataTagDao = new FavoriteDataTagDaoImpl();@Overridepublic void clip(FavoriteDataDto favoriteDataDto) {this.favoriteDataDao.save(favoriteDataDto);}@Overridepublic void unclip(String userId, String dataId) {FavoriteDataDto favoritedataKeyDto = FavoriteDataDto.builder().userId(userId).dataId(dataId).build();FavoriteDataDto favoriteDataDto = favoriteDataDao.load(favoritedataKeyDto);List tagIds = dataTagDao.getTagIdsForData(userId, dataId, true);List dataTagKeyDtos = tagIds.stream().map(tagId -> FavoriteDataTagDto.builder().userId(userId).dataId(dataId).tagId(tagId).build()).collect(Collectors.toList());List dataTagDtos = dataTagDao.batchLoad(dataTagKeyDtos);long ttl = new Date().getTime() / 1000 + 60;favoriteDataDao.delete(favoritedataKeyDto);FavoriteDataHistoryDto historyDto = FavoriteDataHistoryDto.builder().userId(favoriteDataDto.getUserId()).dataId(favoriteDataDto.getDataId()).dataType(favoriteDataDto.getDataType()).creatorId(favoriteDataDto.getCreatorId()).title(favoriteDataDto.getTitle()).contentUrl(favoriteDataDto.getContentUrl()).thumbnailUrl(favoriteDataDto.getThumbnailUrl()).clipTime(favoriteDataDto.getClipTime()).ttl(ttl).build();favoriteDataHistoryDao.save(historyDto);dataTagDtos.forEach(dataTagDto -> dataTagDto.setTtl(ttl));dataTagDao.batchSave(dataTagDtos);}@Overridepublic void undoUnclip(String userId, String dataId) {FavoriteDataHistoryDto historyKeyDto = FavoriteDataHistoryDto.builder().userId(userId).dataId(dataId).build();FavoriteDataHistoryDto historyDto = favoriteDataHistoryDao.load(historyKeyDto);List tagIds = dataTagDao.getTagIdsForData(userId, dataId, true);List dataTagKeyDtos = tagIds.stream().map(tagId -> FavoriteDataTagDto.builder().userId(userId).dataId(dataId).tagId(tagId).build()).collect(Collectors.toList());List dataTagDtos = dataTagDao.batchLoad(dataTagKeyDtos);favoriteDataHistoryDao.delete(historyKeyDto);FavoriteDataDto favoriteDataDto = FavoriteDataDto.builder().userId(historyDto.getUserId()).dataId(historyDto.getDataId()).dataType(historyDto.getDataType()).creatorId(historyDto.getCreatorId()).title(historyDto.getTitle()).contentUrl(historyDto.getContentUrl()).thumbnailUrl(historyDto.getThumbnailUrl()).clipTime(historyDto.getClipTime()).build();favoriteDataDao.save(favoriteDataDto);dataTagDtos.forEach(dataTagDto -> dataTagDto.setTtl(null));dataTagDao.batchSave(dataTagDtos);}}
完整代码参考
GitHub - JessicaWin/dynamodb-in-action at ttl-usage
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
