使用DynamoDbIndex.scan()带过滤表达式无结果问题排查
问题描述
我使用如下Java实体类对应DynamoDB表:
@DynamoDbBean @Data public class RecordDynamoDB { private String id; private String customerIdHash; private OffsetDateTime expirationTime; @DynamoDbPartitionKey public String getId() { return id; } @DynamoDbSecondarySortKey(indexNames = {"expirationTime-index"}) @DynamoDbConvertedBy(DynamoDBTimestampConverter.class) public OffsetDateTime getExpirationTime() { return expirationTime; } @DynamoDbSecondaryPartitionKey(indexNames = "customerIdHash-index") public String getCustomerIdHash() { return customerIdHash; } }
原扫描代码可正常查询符合指定状态和过期时间的记录:
var attributeValueMap = Map.of( ":status", AttributeValue.fromS(Status.EXPIRED.getValue()), ":expirationTime", DATE_TIME_CONVERTER.transformFrom(now) ); var scanRequest = ScanRequest.builder() .tableName(TABLE_NAME) .filterExpression("expirationTime < :expirationTime AND grant.#status = :status") .expressionAttributeValues(attributeValueMap) .expressionAttributeNames(Map.of("#status", "status")) .build(); return dynamoDbClient.scan(scanRequest) .items() .stream() .map(recordTableSchema::mapToItem) .toList();
为提升查询性能,我创建了名为expirationTime-index的GSI,以customerIdHash为分区键、expirationTime为排序键,改写代码使用该索引扫描并携带过滤表达式,但未返回任何记录。即使更新customerIdHash的注解指定该GSI后问题仍存在,且索引扫描返回空页,而表本身有48条记录,请问问题出在哪里?
问题原因分析
- GSI注解配置不匹配:实体类中
customerIdHash的@DynamoDbSecondaryPartitionKey指定的索引是customerIdHash-index,而expirationTime的@DynamoDbSecondarySortKey指定的是expirationTime-index,两个字段未关联到同一个GSI,导致你创建的expirationTime-index无法正确映射实体类的键结构。 - GSI未投影过滤所需属性:DynamoDB的GSI仅包含主键属性(分区+排序)和显式配置的投影属性。你的过滤条件用到了
status字段,但如果GSI没有投影该字段,扫描时无法读取到status值,自然会过滤掉所有结果。 - GSI数据未同步:如果GSI是新创建的,或者之前注解配置错误导致数据未同步到GSI,索引中会没有数据,扫描返回空页。
解决方案
修正GSI实体类注解
将customerIdHash的注解索引名称改为expirationTime-index,确保两个字段属于同一个GSI:@DynamoDbSecondaryPartitionKey(indexNames = "expirationTime-index") public String getCustomerIdHash() { return customerIdHash; } @DynamoDbSecondarySortKey(indexNames = {"expirationTime-index"}) @DynamoDbConvertedBy(DynamoDBTimestampConverter.class) public OffsetDateTime getExpirationTime() { return expirationTime; }同时确保DynamoDB控制台或基础设施代码中,
expirationTime-index的分区键为customerIdHash、排序键为expirationTime。配置GSI投影属性
确保expirationTime-index投影了status字段:- 控制台创建/修改GSI时,将投影类型设置为
ALL,或显式添加status到投影属性列表; - 若使用注解驱动,可添加
@DynamoDbIndexProjection指定投影属性。
- 控制台创建/修改GSI时,将投影类型设置为
验证GSI数据同步
等待GSI状态变为ACTIVE(可通过DynamoDB控制台查看),确认现有数据已同步到索引中。修正扫描请求
扫描时明确指定索引名称:var attributeValueMap = Map.of( ":status", AttributeValue.fromS(Status.EXPIRED.getValue()), ":expirationTime", DATE_TIME_CONVERTER.transformFrom(now) ); var scanRequest = ScanRequest.builder() .tableName(TABLE_NAME) .indexName("expirationTime-index") // 指定目标GSI .filterExpression("expirationTime < :expirationTime AND #status = :status") .expressionAttributeValues(attributeValueMap) .expressionAttributeNames(Map.of("#status", "status")) .build(); return dynamoDbClient.scan(scanRequest) .items() .stream() .map(recordTableSchema::mapToItem) .toList();
内容的提问来源于stack exchange,提问作者Sergey Tsypanov
相关产品推荐
相关产品推荐

