#amazon-dynamodb #dynamodb-queries
#amazon-dynamodb #dynamodb-запросы
Вопрос:
У нас есть таблица 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 и запрашиваем для каждого года в отдельном потоке, а затем объединяем результат, возвращаемый каждым потоком.
Следующий код используется для запроса для одного 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;
}
Во время нагрузочного тестирования наши запросы начинают занимать больше времени по мере загрузки большего количества данных в таблицу.
Для 200 ТЫСЯЧ записей и около того, а диапазон дат рождения составляет около 25 лет, это занимает приблизительно 2-3 секунды.
Если мы увеличим количество записей в таблице до 1,5 миллионов, это начнет занимать около 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
Комментарии:
1. событие, если вы сможете заставить его работать, он будет не очень производительным, поскольку большинство ваших вариантов использования — join, вам, вероятно, следует использовать sql db вместо nosql
Ответ №1:
Каков диапазон времени входа в систему? Рассмотрите возможность переключения gsi на индекс при входе в систему, если он уже, чем диапазон DateOfBirth.
При 1,5 миллионах записей и запросах за 25 лет каждый запрос должен будет прочитать около 60 000 записей, что займет некоторое время, поскольку каждая страница загружается последовательно. Вы можете распараллелить запрос каждого года, одновременно запрашивая часть каждого года, разделяя диапазон между startDateDob
и endDateDob
на меньшие неперекрывающиеся диапазоны. например, если startDateDob и endDateDob охватывают целый год, разделите на 12 запросов, по одному на каждый месяц.
Комментарии:
1. Мы запустили наш код и вывели время, затраченное запросом за каждый год, результаты прикреплены в edit. Как вы можете ясно видеть из результатов, что в каком-то году содержится больше записей, для этого года время, затрачиваемое запросом, также увеличивается. У нас сложилось впечатление, что DynamoDB не увеличит задержку запроса, даже если объем данных в разделе увеличится за счет увеличения RCU.
2. Сколько раз более длинные запросы разбиваются на страницы? Если вы разделите и распараллелите запросы за год, вы сможете получить необходимую скорость.