Как запросить массив значений первичного ключа в DynamodB
У меня есть одна таблица в AWS Dynamodb с 1 миллионом записей. Можно ли запросить массив значений первичного ключа в одном запросе с дополнительным условием ключа сортировки в Dynamodb? Я использую для своей серверной логики.
Вот парамс
var params = {
TableName: "client_logs",
KeyConditionExpression: "#accToken = :value AND ts between :val1 and
:val2",
ExpressionAttributeNames: {
"#accToken": "acc_token"
},
ExpressionAttributeValues: {
":value": clientAccessToken,
":val1": parseInt(fromDate),
":val2": parseInt(toDate),
":status":confirmStatus
},
FilterExpression:"apiAction = :status"
};
Здесь acc_token является первичным ключом, и я хочу запросить массив значений access_token в одном запросе.
2 ответа
Нет, это невозможно. Один запрос может искать только одно конкретное значение хеш-ключа. (См. DynamoDB - Запрос.)
Однако вы можете выполнять несколько запросов параллельно, что даст желаемый эффект.
Изменить (2018-11-21)
Поскольку вы сказали, что вам нужно более 200 хеш-ключей, вот два возможных решения. Эти решения не требуют неограниченных параллельных вызовов DynamoDB, но они будут стоить вам больше RCU. Они могут быть быстрее или медленнее, в зависимости от распределения данных в вашей таблице.
Я не знаю, как распределяются ваши данные, поэтому я не могу сказать, какой из них лучше для вас. Во всех случаях мы не можем использовать acc_token
в качестве ключа сортировки GSI, потому что вы не можете использовать IN
оператор в выражении KeyConditionExpression. (См. DynamoDB - Условие.)
Решение 1
Эта стратегия основана на глобальном вторичном индексе записи для запросов выборочных таблиц
шаги:
- Добавьте новый атрибут к элементам, которые вы записываете в свою таблицу. Этот новый атрибут может быть числом или строкой. Давайте назовем это
index_partition
, - Когда вы записываете новый элемент в вашу таблицу, присвойте ему случайное значение из
0
вN
заindex_partition
, (Вот,N
некоторая произвольная константа по вашему выбору.9
это, вероятно, нормальное значение для начала.) - Создать GSI с хеш-ключом
index_partition
и ключ сортировкиts
, Вам нужно будет спроектироватьapiAction
а такжеacc_token
в GSI. - Теперь вам нужно только выполнить
N
запросы. Используйте ключевое условие выраженияindex_partition = :n AND ts between :val1 and :val2
и выражение фильтраapiAction = :status AND acc_token in :acc_token_list
Решение 2
Это решение аналогично последнему, но вместо случайного шардинга GSI мы будем использовать раздел GSI на основе даты.
шаги:
- Добавьте новый строковый атрибут к элементам, которые вы записываете в свою таблицу. Давайте назовем это
ts_ymd
, - Когда вы пишете новый элемент в вашу таблицу, используйте только
yyyy-mm-dd
частьts
установить значениеts_ymd
, (Вы можете использовать любую гранулярность, которая вам нравится. Это зависит от вашего типичного диапазона запросов дляts
, Если:val1
а также:val2
обычно находятся всего в часе друг от друга, тогда подходящим ключом раздела GSI может быть гггг-мм-дд-чч.) - Создать GSI с хеш-ключом
ts_ymd
и ключ сортировкиts
, Вам нужно будет спроектироватьapiAction
а такжеacc_token
в GSI. - Предполагая, что вы использовали yyyy-mm-dd для ключа раздела GSI, вам нужно выполнять только один запрос на каждый день, который находится в пределах
:val1
а также:val2
, Используйте ключевое условие выраженияts_ymd = :ymd AND ts between :val1 and :val2
и выражение фильтраapiAction = :status AND acc_token in :acc_token_list
Решение 3
Я не знаю, сколько разных значений apiAction
Есть и как эти значения распределяются, но если их несколько, и они имеют примерно одинаковое распределение, вы можете разделить GSI на основе этого значения. Чем больше возможных значений у вас есть для apiAction
Чем лучше это решение для вас. Ограничивающим фактором здесь является то, что вам нужно иметь достаточно значений, чтобы вы не столкнулись с пределом в 10 ГБ для вашего GSI.
шаги:
- Создать GSI с хеш-ключом
apiAction
и ключ сортировкиts
, Вам нужно будет спроектироватьacc_token
в GSI. - Вам нужно выполнить только один запрос. Используйте ключевое условие выражения
apiAction = :status AND ts between :val1 and :val2" and a filter expression of
acc_token in: acc_token_list`.
Для всех этих решений следует учитывать, насколько равномерно будет распределяться ключ раздела GSI, и размер типичного диапазона для ts
в вашем запросе. Вы должны использовать выражение фильтра на acc_token
, поэтому вы должны попытаться выбрать решение, которое минимизирует общее количество элементов, которые будут соответствовать вашему выражению условия ключа, но в то же время вы должны знать, что для одного ключа раздела не может быть более 10 ГБ данных (для таблицы или для GSI). Вы также должны помнить, что GSI может быть запрошен только как окончательно согласованное чтение.
Вы можете эффективно выполнять как запрос диапазона ключей раздела, так и применять дополнительное условие к ключу сортировки с помощью запроса PartiQL SELECT . Официальная документация DDB гласит:
Чтобы гарантировать, что инструкция SELECT не приведет к полному сканированию таблицы, условие предложения WHERE должно указывать ключ секции. Используйте оператор равенства или IN.
В документации не упоминается конкретно ключ сортировки, но говорится, что дополнительная фильтрация по неключевому атрибуту по-прежнему НЕ приводит к полному сканированию. Так что я почти уверен, что условие по ключу сортировки с одним из поддерживаемых операторов не вызовет сканирования таблицы, выполняется быстро и потребляет как можно меньше единиц емкости.
Таким образом, ваш запрос может выглядеть так:
SELECT * FROM client_logs WHERE acc_token IN (t1, t2, ...) AND ts BETWEEN t1 AND t2
Примеры Node.js использования PartiQL API можно найти здесь .