Запит DynamoDB за датою


102

Я походжу з реляційної бази даних і намагаюся працювати з DynamoDB від Amazon

У мене є таблиця з хеш-ключем "DataID" і діапазоном "CreatedAt", а також безліччю елементів у ній.

Я намагаюся отримати всі елементи, які були створені після певної дати та відсортовані за датою. Що досить просто в реляційній базі даних.

У DynamoDB найближче, що я міг знайти, - це запит та використання ключа діапазону, більшого за фільтр. Єдина проблема полягає в тому, що для виконання запиту мені потрібен хеш-ключ, який перешкоджає меті.

То що я роблю не так? Чи неправильна моя схема таблиці, чи не повинен хеш-ключ бути унікальним? чи існує інший спосіб запиту?

Відповіді:


34

Оновлена ​​відповідь:

DynamoDB дозволяє вказати вторинні індекси, щоб допомогти у цьому запиті. Вторинні індекси можуть бути як глобальними, що означає, що індекс охоплює всю таблицю по хеш-ключах, так і локальним, що означає, що індекс буде існувати в кожному розділі хеш-ключа, таким чином вимагаючи, щоб хеш-ключ також вказувався під час запиту.

Для випадку використання цього питання ви хотіли б використовувати глобальний вторинний індекс у полі "CreatedAt".

Докладніше про вторинні індекси DynamoDB див. Документацію щодо вторинного індексу

Оригінальна відповідь:

DynamoDB не дозволяє індексувати пошук лише за ключем діапазону. Необхідний хеш-ключ, щоб служба знала, в який розділ шукати дані.

Звичайно, ви можете виконати операцію сканування для фільтрації за значенням дати, однак для цього знадобиться повне сканування таблиці, тому це не ідеально.

Якщо вам потрібно виконати індексований пошук записів за часом за кількома первинними ключами, DynamoDB може не бути ідеальною послугою для вас, або вам може знадобитися використовувати окрему таблицю (або в DynamoDB, або в реляційному сховищі) для зберігання елемента метадані, щодо яких ви можете виконати індексований пошук.


14
Див. Коментарі до відповіді нижче; зараз неможливо впоратись із цим, принаймні не для того, що просив ОП. GSI все ще вимагають, щоб ви вказали хеш-ключ, тому ви не можете робити запити для всіх записів із величиною CreatedAtбільше певної точки.
пкадінг

4
@pkaeding - це правильно. Ви можете отримати записи старше певної дати за допомогою сканування , але ви не можете отримати їх у відсортованому порядку. GSI не допоможе вам у цьому випадку. Неможливо сортувати ключ розділу , а також не можна запитувати лише ключ діапазону .
gkiko

15
Для тих, хто вас бентежить. ЦЕ ВІДПОВІДЬ НЕПРАВИЛЬНА. Його оригінальна відповідь правильна, але оновлена ​​відповідь - ні. Відповідь Уорена Парада читайте нижче. Це правильно.
Райан Шилінгтон,

1
@MikeBrant Я хочу зробити запит (а не сканувати, який переглядає кожен елемент таблиці, роблячи його дуже неефективним та дорогим) таблицю в хеш-ключі GSI таблиці (CreatedAt), використовуючи символ більше, ніж. Наскільки мені відомо, цього зробити не можна.
Азіз Джавед

4
Проблема, яку ви, мабуть, отримуєте під час використання дати як основного розділу, полягає в тому, що ви можете створити точку доступу на певному або одному з однолітків через те, що в більшості сховищ даних нові дані запитуються частіше, ніж старі дані.
Знання

53

Враховуючи вашу поточну структуру таблиці, це наразі неможливо в DynamoDB. Величезною проблемою є розуміння того, що клавіша хеш таблиці (розділу) повинна розглядатися як створення окремих таблиць. У чомусь це дійсно потужне (уявіть, що розділові ключі створюють нову таблицю для кожного користувача чи клієнта тощо ...).

Запити можна робити лише в одному розділі. Це справді кінець історії. Це означає, що якщо ви хочете зробити запит за датою (ви хочете використовувати msec з епохи), тоді всі елементи, які ви хочете отримати в одному запиті, повинні мати однаковий хеш (ключ розділу).

Я повинен це кваліфікувати. Ви абсолютно можете scanза критерієм, який ви шукаєте, це не проблема, але це означає, що ви будете переглядати кожен окремий рядок у вашій таблиці, а потім перевіряти, чи є в цьому рядку дата, яка відповідає вашим параметрам. Це дійсно дорого, особливо якщо ви займаєтеся спочатку збереженням подій за датою (тобто у вас багато рядків).

У вас може виникнути спокуса розмістити всі дані в одному розділі для вирішення проблеми, і ви цілком можете, однак ваша пропускна здатність буде болісно низькою, враховуючи, що кожен розділ отримує лише частку від загальної встановленої суми.

Найкраще, що потрібно зробити, це визначити більше корисних розділів для створення для збереження даних:

  • Вам дійсно потрібно переглянути всі рядки, чи це лише рядки певного користувача?

  • Чи було б добре спочатку звузити список за місяцями та зробити кілька запитів (по одному на кожен місяць)? Або за роками?

  • Якщо ви проводите аналіз часових рядів, є кілька варіантів, змініть розділовий ключ на щось обчислюване, PUTщоб полегшити його query, або використовуйте інший продукт aws, такий як kinesis, який дозволяє реєструвати лише додавання.


4
Я хочу наголосити на варіанті, який ви висунули у своєму останньому пункті щодо розгляду "за роками". Створіть атрибут like yyyyта хеш для цього, але також створіть createdдату, яку можна використовувати як ключ діапазону. Тоді ви отримуєте 10 ГБ даних на рік (27 МБ на день), що, мабуть, чудово для інших обставин. Це означає, що вам потрібно створювати запит на рік, коли запити дат переходять межу року, однак, принаймні це спрацює, і це безпечніше, ніж створення фіктивного хеш-ключа.
Райан Шиллінгтон,


1
як пояснюється у наведеному вище посиланні, суворо часові розділові клавіші можуть призвести до гарячих точок. якщо вам потрібно використовувати ключі розділів, засновані на часі, краще додати до ключа розділу якийсь інший елемент, щоб розподілити період часу по декількох розділах. Я бачив пропозиції просто використовувати префікс між 0-n, де n - кількість розділів кожного разу, коли сегмент повинен бути розподілений.
дрес

@RyanShillington Не існує обмеження на 10 Гб для вторинних глобальних індексів. Це обмеження стосується лише місцевих вторинних індексів.
Саймон Форсберг,

18

Підхід, який я застосував для вирішення цієї проблеми, полягає у створенні Глобального вторинного індексу, як показано нижче. Не впевнений, що це найкращий підхід, але сподіваємось, чи корисний він комусь.

Hash Key                 | Range Key
------------------------------------
Date value of CreatedAt  | CreatedAt

Обмеження, накладене на користувача HTTP API, визначати кількість днів для отримання даних, встановлене за замовчуванням до 24 годин.

Таким чином, я завжди можу вказати HashKey як день поточної дати, а RangeKey може використовувати оператори> та <під час отримання. Таким чином, дані також розповсюджуються по кількох осколках.


8

Ваш ключ хешу (первинний сортування) повинен бути унікальним (якщо у вас немає діапазону, як зазначено іншими).

У вашому випадку для запиту таблиці ви повинні мати вторинний індекс.

|  ID  | DataID | Created | Data |
|------+--------+---------+------|
| hash | xxxxx  | 1234567 | blah |

Ваш хеш-ключ є ідентифікатором. Ваш вторинний індекс визначається як: DataID-Created-index (це ім'я буде використовувати DynamoDB)

Потім ви можете зробити такий запит:

var params = {
    TableName: "Table",
    IndexName: "DataID-Created-index",
    KeyConditionExpression: "DataID = :v_ID AND Created > :v_created",
    ExpressionAttributeValues: {":v_ID": {S: "some_id"},
                                ":v_created": {N: "timestamp"}
    },
    ProjectionExpression: "ID, DataID, Created, Data"
};

ddb.query(params, function(err, data) {
    if (err) 
        console.log(err);
    else {
        data.Items.sort(function(a, b) {
            return parseFloat(a.Created.N) - parseFloat(b.Created.N);
        });
        // More code here
    }
});

По суті, ваш запит виглядає так:

SELECT * FROM TABLE WHERE DataID = "some_id" AND Created > timestamp;

Вторинний індекс збільшить необхідні одиниці ємності читання / запису, тому вам потрібно це врахувати. Це все-таки набагато краще, ніж виконувати сканування, яке буде дорогим при читанні та в часі (і, я вважаю, обмежується 100 елементами).

Це може бути не найкращим способом зробити це, але для тих, хто звик до РД (я також звик до SQL), це найшвидший спосіб отримати продуктивність. Оскільки щодо схеми немає обмежень, ви можете створити щось, що працює, і коли у вас є пропускна здатність, щоб працювати найбільш ефективно, ви можете змінити ситуацію навколо.


1
Ви говорите, що обмежень немає, але ви повинні знати, що такий підхід означає, що ви можете зберегти максимум 10 ГБ даних (максимум для одного розділу).
Райан Шилінгтон,

Це був би підхід, якби DataID був відомий. Але тут нам потрібно отримати кожен рядок, для якого створене більше, ніж якась дата.
Ясіт Прабуддхака

3

Ви можете зробити клавішу Hash щось на зразок ідентифікатора "категорії товару", а потім ключ діапазону як комбінацію мітки часу з унікальним ідентифікатором, доданим в кінці. Таким чином ви знаєте хеш-ключ і все ще можете запитувати дату з величиною більше.


1

Ви можете мати кілька однакових хеш-ключів; але лише якщо у вас є ключ діапазону, який змінюється. Думайте про це як про формати файлів; Ви можете мати 2 файли з однаковою назвою в одній папці, якщо їх формат відрізняється. Якщо їх формат однаковий, їх назва повинна бути іншою. Те саме поняття стосується ключів хешу / діапазону DynamoDB; просто подумайте про хеш як назву, а про діапазон як формат.

Крім того, я не пам’ятаю, чи були вони під час ОП (я не вірю, що вони мали), але зараз вони пропонують місцеві вторинні індекси.

Я розумію це, що тепер це має дозволити вам виконувати бажані запити без необхідності робити повне сканування. Недоліком є ​​те, що ці індекси повинні бути вказані під час створення таблиці, а також (я вважаю) не можуть бути пустими при створенні елемента. Крім того, вони вимагають додаткової пропускної здатності (хоча, як правило, не стільки, скільки сканування) та зберігання, тому для деяких це не ідеальне рішення, а життєздатна альтернатива.

Однак я все-таки рекомендую відповідь Майка Бранта як найкращий метод використання DynamoDB; і використовую цей метод сам. У моєму випадку у мене просто є центральна таблиця, в якій як мій ідентифікатор є лише хеш-ключ, потім вторинні таблиці, які мають хеш і діапазон, які можна запитувати, тоді елемент вказує код на "предмет, що цікавить" центральної таблиці, безпосередньо .

Додаткові дані про вторинних індексів можна знайти в документації DynamoDB Амазонки тут для тих , хто зацікавлений.

У будь-якому випадку, сподіваємось, це допоможе будь-кому іншому, що трапляється в цій темі.


Я спробував створити таблицю DynamoDB, де був AWSDynamoDBKeySchemaElement 'createdAt' в типе хеш і знову AWSDynamoDBKeySchemaElement 'createdAt' в діапазоні типів, і я отримав помилку, в якій говорилось, что ошибка Domain = com.amazonaws.AWSDynamoDBErDinDomainDomainDBErDinDomainDomainDBErDinAmoDBErDinDomainDomainDBErDinDomainDomainDBEnaDBEnDomainDomainDomainDomainDomainDomainDomainDomainDomainDenamoDbErEndmentEnmentEnmentEndment = {__ type = com.amazon.coral.validate # ValidationException, message = І хеш-ключ, і елемент Range Key у схемі KeySche мають одне і те ж ім'я}. Тому я не вважаю, що ви говорите правильно.
user1709076

Я вважаю, що ви неправильно зрозуміли (хоча, я гадаю, я теж не був дуже чітким у своєму описі). Ви не можете мати в таблиці 2 різних атрибути (стовпці) з однаковим іменем, але коли ви створюєте хеш-ключ за допомогою ключа діапазону, ви можете мати кілька елементів, які всі використовують один і той же хеш, якщо їх діапазон різний, і навпаки. Наприклад: Ваш хеш - "ID", а діапазон - "Date". Ви можете мати 2 екземпляри ідентифікатора "1234", якщо їх дата інша.
DGolberg

Ах Д.Голдберг! Я вас зараз зрозумів. Це чудово. Отже, для мого випадку, оскільки я лише і завжди хочу просто запитувати текстові повідомлення 'after date = x', схоже, я міг би встановити, щоб усі текстові повідомлення мали однаковий 'fake_hash = 1'. Тоді зробіть мій query.keyConditionExpression = @ "fake_hash = 1 і #Date>: val". Велике спасибі. Якщо у вас є якісь інші дані, я був би радий їх почути, оскільки здається дивним мати хеш, який завжди має однакове значення?
user1709076

Мені доведеться ще раз перевірити, але я майже впевнений, що ви можете зробити запит щодо таблиць, що містять лише хеш ... хоча, якщо ви використовуєте як хеш позначку дати / часу, я рекомендую записати до максимально коротка одиниця виміру, наприклад мілісекунди або нано / мікросекунди (якою б не була найменша одиниця часу, яку може записати код), щоб зменшити ймовірність перекриття дати / часу. Крім того, ви можете додати оптимістичне блокування, щоб ще більше зменшити можливість накладання: docs.aws.amazon.com/amazondynamodb/latest/developerguide/ ... Просто повторіть спробу в інший раз, якщо виникне конфлікт.
DGolberg

-11

Оновлена ​​відповідь Немає зручного способу зробити це за допомогою запитів Dynamo DB із передбачуваною пропускною здатністю. Одним (недостатньо оптимальним) варіантом є використання GSI зі штучним HashKey & CreatedAt. Потім запитуйте лише HashKey та згадайте ScanIndexForward, щоб замовити результати. Якщо ви можете придумати природний HashKey (скажімо, категорія товару тощо), тоді цей метод є переможцем. З іншого боку, якщо ви зберігаєте однаковий HashKey для всіх елементів, то це вплине на пропускну здатність, головним чином, коли ваш набір даних перевищує 10 ГБ (один розділ)

Оригінальна відповідь: Ви можете зробити це зараз у DynamoDB, використовуючи GSI. Зробіть поле "CreatedAt" як GSI та видайте запити на зразок (GT some_date). Зберігайте дату як число (мсек з епохи) для запитів такого типу.

Деталі доступні тут: Глобальні вторинні індекси - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html#GSI.Using

Це дуже потужна функція. Майте на увазі, що запит обмежений умовою (EQ | LE | LT | GE | GT | BEGINS_WITH | BETWEEN) - Amazon DynamoDB: http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Condition.html


31
Я проголосував проти, оскільки, наскільки я можу зрозуміти, ваша відповідь неправильна. Подібно до первинного ключа таблиці, ви можете запитувати хеш-ключ GSI лише за допомогою оператора еквалайзера. Якщо ви мали на увазі, що це CreatedAtповинен бути ключ діапазону GSI, то вам потрібно буде вибрати хеш-ключ - і тоді ви повернетесь до того, що почали, оскільки ви зможете запитувати GT CreatedAtлише для певного значення параметра хеш-ключ.
PaF

Погодився з PaF. Використання GSI з хеш-ключем як час створення не допомагає з питаннями, заданими в OP.
4-8-15-16-23-42
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.