我们有一个具有以下结构的dynamoDb表。
userId - partition key- number
yearOfBirth -attribute number
dateOfBirth - attribute(number in millisecond)
loginTime - attribute(number in millisecond)
以及以下gsi-user_gsi
yearOfBirth - partition key- number
dateOfBirth - sort key (number -in millisecond)
loginTime - attribute(number in millisecond)
我们正在使用java aws sdk查询表。 我们的查询要求是查询两个dateOfBirth之间和两个loginTime之间的所有用户。 我们从dateofBirth范围中获取所有年份,并在单独的线程中查询每年,然后加入每个线程返回的结果。
以下代码用于查询单个yearOfBirth-
public Set<Long> queryForSingleBirthYear(Long startDateDob, Long endDateDob,Long minLoginTime, Long maxLoginTime, int yearOfBirth){
Set<Long> userIds = new HashSet<>();
Map<String, AttributeValue> lastEvaluatedKey = null;
do{
QueryRequest queryRequest = new QueryRequest().withTableName("user");
queryRequest
.withIndexName("user_gsi")
.withExclusiveStartKey(lastEvaluatedKey);
Condition keyCond = new Condition().withComparisonOperator(ComparisonOperator.EQ)
.withAttributeValueList(new AttributeValue().withN(Integer.toString(yearOfBirth)));
String startDate = startDateDob.toString();
String endDate = endDateDob.toString();
Condition dobCond = new Condition().withComparisonOperator(ComparisonOperator.BETWEEN)
.withAttributeValueList(new AttributeValue().withN(startDate), new AttributeValue().withN(endDate));
Map<String, Condition> keyCondMap = new HashMap<>();
keyCondMap.put("yearOfBirth", keyCond);
keyCondMap.put("dateOfBirth", dobCond);
queryRequest.setKeyConditions(keyCondMap);
Map<String,String> attrNames = new HashMap<>();
attrNames.put("#loginTime","loginTime");
Map<String,AttributeValue> attrvalues = new HashMap<>();
attrvalues.put(":v_minLoginTime",new AttributeValue().withN(minLoginTime.toString()));
attrvalues.put(":v_maxLoginTime",new AttributeValue().withN(maxLoginTime.toString()));
String queryFilter = "#loginTime > :v_minLoginTime and #loginTime <= :v_maxLoginTime";
queryRequest.withFilterExpression(queryFilter)
.withExpressionAttributeNames(attrNames)
.withExpressionAttributeValues(attrvalues);;
QueryResult queryResult = amazonDynamoDB.query(queryRequest);
List<Map<String, AttributeValue>> items = queryResult.getItems();
for (Map<String, AttributeValue> item : items) {
String id = item.get("userId").getN();
userIds.add(Long.valueOf(id));
}
lastEvaluatedKey = queryResult.getLastEvaluatedKey();
}while (lastEvaluatedKey != null);
return basicFilterRes;
}
在进行负载测试时,随着将更多数据加载到表中,查询开始花费时间。 对于200K条记录以及大约25年的出生日期范围的dateOfBirth,大约需要2-3秒。 如果我们将表中的记录数增加到150万,则开始大约需要15-20秒。我们尝试增加RCU甚至将RCU更改为按需模式,但是时间保持不变。
编辑
以下是查询每年打印时间后的结果-
yearOfBirth=1972, resultSize=110, timeMs=56
yearOfBirth=1977, resultSize=199, timeMs=54
yearOfBirth=1971, resultSize=89, timeMs=59
yearOfBirth=1973, resultSize=113, timeMs=60
yearOfBirth=1974, resultSize=143, timeMs=60
yearOfBirth=1978, resultSize=266, timeMs=59
yearOfBirth=1998, resultSize=3524, timeMs=612
yearOfBirth=1993, resultSize=3923, timeMs=677
yearOfBirth=1995, resultSize=4569, timeMs=714
yearOfBirth=1994, resultSize=4688, timeMs=777
答案 0 :(得分:1)
loginTime范围是多少?如果gsi小于dateOfBirth范围,请考虑将gsi切换为indexTime上的索引。
具有150万条记录和25年的查询,每个查询将必须读取约60,000条记录,这需要花费一些时间,因为每个页面都是按顺序加载的。通过将startDateDob
和endDateDob
之间的范围分成较小的非重叠范围,可以同时查询每年的一部分,从而使每年查询并行化。例如如果startDateDob和endDateDob涵盖整个一年,则分为12个查询,每个月查询一次。