Итак, вам нужно надежное решение, быстрое реагирование на тысячи запросов в секунду, вы выбираете БД без SQL, и AWS DynamoDB - очаровательный принц: отличная производительность, неограниченное масштабирование и полное управление.
Но со временем у вас будет все больше и больше таблиц DynamoDB, у вас есть несколько регионов, первичный ключ (например, userId) дублируется на несколько таблиц, и приложению внезапно потребовалось запросить данные с использованием полей, которых вы не ожидали ...
Он бизнес начинает задавать сложные вопросы о данных. Вам нужно подсчитывать, суммировать, фильтровать, группировать данные и объединять таблицы. Разработчики приложений начинают запрашивать слишком много таблиц и выполнять вычисления и соединения в коде или, возможно, экспортировать данные в Excel, чтобы предоставить ответы ???
ммм ... очаровательный принц сейчас уже не так светит: - \
В этой статье, состоящей из трех частей, я продемонстрирую, как вы можете решить эти три ключевые проблемы:
1. Запросы DynamoDB с использованием AWS Javascript SDK, знание ключей и индексов и запрос против сканирования
2. Как использовать простой синтаксис SQL для запроса DynamoDB и как подключиться к нему с помощью стандартного клиента MySQL.
3. Как запросить вашу DynamoDB, как если бы это была настоящая реляционная база данных (RDBMS):
- Используйте функции агрегирования (количество, мин, макс…) с помощью группировать по
- Таблицы JOIN и UNION DynamoDB
- Межрегиональные запросы
Часть 1. Запрос DynamoDB с помощью AWS Javascript SDK, знание ключей и индексов, а также запрос и сканирование
Прежде чем выбрать, какой DynamoDB API использовать, вы должны хорошо знать ключи своей таблицы:
◦ Что такое первичный ключ и его ключ сортировки?
◦ Что такое индексы? Каков ключ сортировки каждого индекса?
После ответа на эти вопросы нам нужно решить, какой API использовать: Сканировать или Запрос:
В идеале мы хотели бы использовать Запрос в качестве первого варианта: это самый быстрый способ запросить динамо-машину и получить результаты за доли секунды. Если это невозможно, мы будем использовать Сканирование, которое может занять несколько секунд или даже больше, в зависимости от размера таблицы.
Фильтрация Сканирование проста, вы можете использовать любой столбец (ключевой, сортировка, индекс, сортировка индекса или простой столбец) и иметь несколько фильтров в одном API.
Чтобы запросить, как упоминалось выше, необходимо учитывать структуру ключей таблицы.
Вот три практических правила:
◦ Запрос может содержать одно или два поля для фильтрации (но не более!)
◦ Одно из полей фильтра должно быть первичным ключом или индексным ключом.
◦ Второе поле фильтра должно быть связанным ключом сортировки.
Вот пример таблицы под названием testTable:
У него есть первичный ключ (id1) со связанным ключом сортировки (sort1),
и два индекса со своими собственными связанными ключами сортировки: id2 с sort2 и id3 с sort3. Id4 - это обычное поле.
вы можете увидеть первичный ключ и ключ сортировки на вкладке «Обзор» в консоли AWS.
Индексы и их рабочие ключи можно увидеть на вкладке индексов.
или вы можете получить эту информацию с помощью API describeTable SDK DynamoDB:
it("describe dynamo table", async () => { let table = "testTable"; let params = { TableName: table }; let [err,tableInfo] = await new Promise((resolved) => { let dynamodb = new AWS.DynamoDB({region: "us-west-1"}); dynamodb.describeTable(params, function (err, data) { if (err) { console.log(`ERROR: ${err}`); } resolved([err, data]); }); }); console.log(tableInfo) });
Вот результат:
Давайте посмотрим, какие фильтры запросов мы можем выполнить для этой таблицы:
- Ключ (id1)
- Ключ (id1) И sortKey (sort1)
- Индекс1 (id2)
- Индекс1 (id2) И index1sort (sort2)
- Индекс2 (id3) И index2sort2 (sort3)
Но мы можем НЕ запрашивать:
- SortKey (sort1) - ›без ключа
- Index1sort (sort2) - ›сортировка без ключа индекса
- Ключ И индекс (id1, id2) - ›может быть по ключу или индексу, но не по обоим
- Ключ И index1sort (id1, sort2) - ›ключ может быть только с соответствующей сортировкой
- Index1 AND index2sort (id2, sort3) - ›смешанный индекс и сортировка
- Key1 AND sortKey AND index (id1, sort1, id2) - ›более 2 фильтров
Теперь, когда мы выбрали правильный API для нашего варианта использования, давайте посмотрим на реализацию с использованием AWS SDK для JavaScript:
- запрос с ключом и сортировкой:
// select id1, sort1, id2 from testTable where id1='a' and sort1='a' // using dynamoDB query , key filter it("dynamo basic query - key", async () => { this.AWS = require('aws-sdk'); this.AWS.config.region = 'us-west-1'; let dynamoDB = new AWS.DynamoDB(); let table = "testTable"; let totalRowsCount = 0; let totalRows = []; let [rows, keepWork] = [null,true]; let limit = 1000; // for fields list let fields = "id1, sort1, id2"; // for the query filter "where" let filterValues = { ':v1': {S: 'a'}, ':v2': {S: 'a'}, } // key filter(id1) is mandatory, sort1 is optional let KeyFilterExpression = 'id1 = :v1 and sort1 = :v2' var params = { TableName: table, Limit: limit, ProjectionExpression: fields, ExpressionAttributeValues: filterValues, KeyConditionExpression: KeyFilterExpression } while (keepWork) { //loop to get all results (paging) [rows, keepWork] = await new Promise((resolved) => { dynamoDB.query(params, (err, data) => { if (err) { console.error(err); throw err; } Array.prototype.push.apply(totalRows, data.Items) keepWork = data.LastEvaluatedKey; if (keepWork){ params.ExclusiveStartKey = data.LastEvaluatedKey } resolved([data,keepWork]) }); }) totalRowsCount+= rows.Items.length } console.log(totalRows) })
2. запрос с индексом и сортировкой:
// select id1, sort1, id2 from testTable where id1='a' and sort1='a' // using dyamoDB query, index filter it("dynamo basic query - index", async () => { this.AWS = require('aws-sdk'); this.AWS.config.region = 'us-west-1'; let dynamoDB = new AWS.DynamoDB(); let table = "testTable"; let totalRowsCount = 0; let totalRows = []; let [rows, keepWork] = [null,true]; let limit = 1000; // for fields list let fields = "id1, sort1, id2"; // for the query filter "where" let filterValues = { ':v1': {S: 'a'}, ':v2': {S: 'a'}, } // index key filter(id2) is mandatory, sort2 is optional let IndexFilterExpression = 'id2 = :v1 and sort2 = :v2' var params = { TableName: table, Limit: limit, IndexName: 'id2-sort2-index', ProjectionExpression: fields, ExpressionAttributeValues: filterValues, KeyConditionExpression: IndexFilterExpression } while (keepWork) { //loop to get all results (paging) [rows, keepWork] = await new Promise((resolved) => { dynamoDB.query(params, (err, data) => { if (err) { console.error(err); throw err; } Array.prototype.push.apply(totalRows, data.Items) keepWork = data.LastEvaluatedKey; if (keepWork){ params.ExclusiveStartKey = data.LastEvaluatedKey } resolved([data,keepWork]) }); }) totalRowsCount+= rows.Items.length } console.log(totalRows) })
3. сканирование:
// select id1, sort1, id2 from testTable where id1='a' and sort1='a' // using dynamoDB scan API it("dynamo basic scan", async () => { this.AWS = require('aws-sdk'); this.AWS.config.region = 'us-west-1'; let dynamoDB = new AWS.DynamoDB(); let table = "testTable"; let totalRowsCount = 0; let totalRows = []; let [rows, keepWork] = [null,true]; let limit = 1000; // for fields list let fields = "id1, sort1, id2"; // for the query filter "where" let filterValues = { ':v1': {S: 'a'}, ':v2': {S: 'a'}, } let FilterExpression = 'id1 = :v1 and sort1 = :v2' var params = { TableName: table, Limit: limit, ProjectionExpression: fields, ExpressionAttributeValues: filterValues, FilterExpression: FilterExpression } while (keepWork) { //loop to get all results (paging) [rows, keepWork] = await new Promise((resolved) => { dynamoDB.scan(params, (err, data) => { if (err) { console.error(err); throw err; } Array.prototype.push.apply(totalRows, data.Items) keepWork = data.LastEvaluatedKey; if (keepWork){ params.ExclusiveStartKey = data.LastEvaluatedKey } resolved([data,keepWork]) }); }) totalRowsCount+= rows.Items.length } console.log(totalRows) })
Три длинные функции, показанные выше, представляют собой громоздкий способ DynamoDB выполнять следующие три простых SQL-запроса:
- выберите id1, sort1, id2 из testTable, где id1 = ‘a’ и sort1 = ‘a’
- выберите id1, sort1, id2 из testTable, где id2 = ‘a’ и sort2 = ‘a’
- выберите id1, sort1, id2 из testTable, где id4 = ‘a’ и sort1 = ‘a’
Заключение:
Dynamo - отличное решение noSQL, но логика ключей / индексов не была бы такой тривиальной, если бы вы могли использовать SQL. API слишком длинные и сложные.
Мой список желаний от DynamoDB следующий:
1. Упростите API с помощью синтаксиса SQL.
2. Сделайте все возможное (за кулисами !!), чтобы использовать Запрос, а если других вариантов нет, верните результаты с помощью Сканировать. Автоматически выполняйте все ключи, индексы и логику сортировки за меня.
3. Разрешить все функции реляционной БД:
а. Функции агрегирования, такие как количество, мин, макс…, сгруппировать по и по…
б. Разрешите ПРИСОЕДИНЯТЬ две разные таблицы DynamoDB в регионах и между ними.
c. Разрешить ОБЪЕДИНЕНИЕ результатов запроса из разных таблиц DynamoDB и регионов
г. Подключитесь к DynamoDB с помощью стандартного клиента MySQL
Вы не поверите, все это возможно! Но это для Частей II и III этой серии.
Не могу дождаться? Хотите подсказку? superQuery творит чудеса!
Будьте на связи!