Как рекомендуется удалить большое количество элементов из DynamoDB?

112

Пишу простой сервис логирования в DynamoDB.

У меня есть таблица журналов, в которой используется хэш user_id и диапазон временных меток (Unix epoch int).

Когда пользователь службы закрывает свою учетную запись, мне нужно удалить все элементы в таблице, независимо от значения диапазона.

Каков рекомендуемый способ выполнения такой операции (имея в виду, что могут быть удалены миллионы элементов)?

Насколько я понимаю, у меня есть следующие варианты:

A: Выполните операцию сканирования, вызывая удаление для каждого возвращенного элемента, пока не останется ни одного элемента.

B: выполнить операцию BatchGet, снова вызывая удаление для каждого элемента, пока не останется ни одного

Мне оба эти варианта кажутся ужасными, так как на них уйдет много времени.

В идеале я хочу вызвать LogTable.DeleteItem (user_id) - без указания диапазона и заставить его удалить все за меня.

Тайлер
источник

Ответы:

52

В идеале я хочу вызвать LogTable.DeleteItem (user_id) - без указания диапазона и заставить его удалить все за меня.

Действительно понятная просьба; Я могу представить себе такие расширенные операции, которые со временем могут быть добавлены командой AWS (у них есть история, когда они начинали с ограниченного набора функций и оценивали расширения на основе отзывов клиентов), но вот что вы должны сделать, чтобы избежать затрат на как минимум полное сканирование:

  1. Используйте Query, а не Scan, чтобы получить все элементы для user_id- это работает независимо от используемого комбинированного первичного ключа хэша / диапазона, потому что HashKeyValue и RangeKeyCondition являются отдельными параметрами в этом API, а первый нацелен только на значение атрибута хэш-компонента составного основной ключ. .

    • Обратите внимание, что здесь вам придется иметь дело с разбиением на страницы API запроса, как обычно, см. Параметр ExclusiveStartKey :

      Первичный ключ элемента, с которого будет продолжен предыдущий запрос. Более ранний запрос может предоставить это значение в качестве LastEvaluatedKey, если эта операция запроса была прервана до завершения запроса; либо из-за размера набора результатов, либо из-за параметра Limit. LastEvaluatedKey может быть передан обратно в новом запросе, чтобы продолжить операцию с этого момента.

  2. Переберите все возвращенные элементы и либо упростите DeleteItem, как обычно

    • Обновление : скорее всего, BatchWriteItem больше подходит для такого случая использования (подробности см. Ниже).

Обновить

Как подчеркнуто ivant , операция BatchWriteItem позволяет вам помещать или удалять несколько элементов в нескольких таблицах за один вызов API [выделено мной] :

Чтобы загрузить один элемент, вы можете использовать PutItem API, а чтобы удалить один элемент, вы можете использовать DeleteItem API. Однако, если вы хотите загрузить или удалить большие объемы данных, например, загрузить большие объемы данных из Amazon Elastic MapReduce (EMR) или перенести данные из другой базы данных в Amazon DynamoDB, этот API предлагает эффективную альтернативу.

Обратите внимание, что это все еще имеет некоторые соответствующие ограничения, в частности:

  • Максимальное количество операций в одном запросе - вы можете указать до 25 операций добавления или удаления; однако общий размер запроса не может превышать 1 МБ (полезная нагрузка HTTP).

  • Не атомарная операция - отдельные операции, указанные в BatchWriteItem, являются атомарными; однако BatchWriteItem в целом представляет собой операцию «максимальных усилий», а не атомарную операцию. То есть в запросе BatchWriteItem некоторые операции могут быть выполнены успешно, а другие - нет. [...]

Тем не менее, это, очевидно, дает потенциально значительную выгоду для подобных случаев.

Штеффен Опель
источник
4
Думаю, имеет смысл использовать пакетное удаление для второго шага (оно «замаскировано» как операция пакетной записи )
ivant
1
@ivant - большое спасибо за подсказку, эта "замаскированная" функция удаления BatchWriteItem действительно ускользнула от меня тогда; Я обновил ответ соответственно.
Steffen Opel
для удаления с BatchWriteItemэлементами необходимо указать черезTableWriteItems
Нил
1
Ссылка на BatchWriteItem теперь docs.aws.amazon.com/amazondynamodb/latest/APIReference/…
Тони
4
Я понимаю, что это устарело, и OP не упомянул конкретный языковой SDK, но в Python есть высокий уровень batch_writer()как часть boto3.resource.TableAPI, который «автоматически обрабатывает буферизацию и отправку элементов в пакетах. Кроме того, средство записи пакетов будет также автоматически обрабатывает любые необработанные элементы и повторно отправляет их по мере необходимости », т.е. это оболочка вокруг BatchWriteItem, которая управляет раздражающими частями. boto3.amazonaws.com/v1/documentation/api/latest/reference/…
Давос
46

Согласно документации DynamoDB, вы можете просто удалить всю таблицу.

Увидеть ниже:

«Удаление всей таблицы значительно более эффективно, чем удаление элементов по одному, что существенно удваивает пропускную способность записи, поскольку вы выполняете столько же операций удаления, сколько операций вставки»

Если вы хотите удалить только часть ваших данных, вы можете создать отдельные таблицы для каждого месяца, года или чего-то подобного. Таким образом, вы можете удалить «последний месяц» и сохранить остальные данные нетронутыми.

Вот как вы удаляете таблицу в Java с помощью AWS SDK:

DeleteTableRequest deleteTableRequest = new DeleteTableRequest()
  .withTableName(tableName);
DeleteTableResult result = client.deleteTable(deleteTableRequest);
Джонатан
источник
8
Мне тоже нравится этот ответ, но будьте осторожны: это может создать много таблиц в вашей системе, и мы платим за предоставление таблиц. Таким образом, вам нужно уменьшить подготовку после окончания месяца (если ваша таблица - в месяц), пока эта таблица не будет удалена.
Sergio MC Figueiredo
2
согласен с этим ответом, он применяется, если вам нужно удалить все записи из таблицы, но здесь спрашивающий хочет удалить записи базы пользователей, а не всю таблицу.
Ихтшам Минхас
1
Наличие отдельной таблицы для каждого пользователя было бы дорогостоящим с учетом цен DynamoDB. Одна таблица в месяц на самом деле только ухудшит ситуацию. Это явно ответ на другую, очень конкретную проблему.
Андре Верланг
11
Удаление таблицы также может быть не привлекательным вариантом, если вы используете автоматическую подготовку, такую ​​как CloudFormation, для управления таблицей как частью стека. Я не знаю простого способа заставить CloudFormation воссоздать таблицу, которую вы удалили вручную.
brabster
2
Такой подход требует довольно много времени для удаления и воссоздания (при необходимости) таблицы, что делает ее недоступной в течение всего времени. В вопросе четко говорится об удалении пользовательских данных, которые было бы непрактично разделить на отдельные таблицы для каждого пользователя.
André Werlang
14

Если вы хотите удалить элементы через некоторое время, например, через месяц, просто используйте опцию Time To Live. Он не будет считать единицы записи.

В вашем случае я бы добавил ttl по истечении срока действия журналов и оставил их после удаления пользователя. TTL гарантирует, что журналы в конечном итоге будут удалены.

Когда для таблицы включено время жизни, фоновое задание проверяет атрибут TTL элементов, чтобы узнать, не истек ли срок их действия.

DynamoDB обычно удаляет просроченные элементы в течение 48 часов после истечения срока действия. Точная продолжительность, в течение которой элемент действительно удаляется после истечения срока, зависит от характера рабочей нагрузки и размера таблицы. Элементы, срок действия которых истек, но не были удалены, по-прежнему будут отображаться при чтении, запросах и сканированиях. Эти элементы все еще можно обновлять, и успешные обновления для изменения или удаления атрибута истечения срока действия будут учитываться.

https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/howitworks-ttl.html

Лукас Лисис
источник
добавление TTL - это «обновление» (операция записи). Я не уверен, что есть какая-то выгода от выполнения «обновления» вместо «удаления».
Tomer
вы можете вставить эти данные с помощью исходной записи и обновить с помощью любого другого действия обновления. Конечно, это не вариант, если у вас есть куча данных, и вы хотите ее удалить. Но это допустимый вариант для случаев, когда вы можете использовать ttl для данных, которые вы вставляете или обновляете.
Lukas Liesis 02
1
Я согласен, если TTL уже настроен и очистка может подождать до 48 часов, это определенно оптимальный вариант. Мои извинения, если я не понял.
Tomer
4

Ответ на этот вопрос зависит от количества предметов, их размера и вашего бюджета. В зависимости от этого у нас есть 3 случая:

1- Количество и размер предметов в таблице не очень много. то, как сказал Штеффен Опель, вы можете использовать запрос вместо сканирования, чтобы получить все элементы для user_id, а затем перебрать все возвращенные элементы и либо облегчить, DeleteItemлибоBatchWriteItem. Но имейте в виду, что здесь вы можете сжечь большую пропускную способность. Например, рассмотрим ситуацию, когда вам нужно удалить 1000 элементов из таблицы DynamoDB. Предположим, что каждый элемент имеет размер 1 КБ, в результате получается около 1 МБ данных. Эта задача массового удаления потребует в общей сложности 2000 единиц емкости записи для запроса и удаления. Чтобы выполнить эту загрузку данных в течение 10 секунд (что даже не считается быстрым в некоторых приложениях), вам нужно будет установить предоставленную пропускную способность записи для таблицы на 200 единиц емкости записи. Как видите, можно использовать этот способ для меньшего количества элементов или элементов небольшого размера.

2- У нас много предметов или очень крупных предметов в таблице, и мы можем хранить их в зависимости от времени в разных таблицах. Затем, как сказал Джонатан, вы можете просто удалить таблицу. это намного лучше, но я не думаю, что это подходит к вашему случаю. Поскольку вы хотите удалить все данные пользователей, независимо от времени создания журналов, в этом случае вы не можете удалить конкретную таблицу. Если вы хотите иметь отдельную таблицу для каждого пользователя, я думаю, что если количество пользователей велико, то это так дорого и непрактично для вашего случая.

3- Если у вас много данных и вы не можете разделить горячие и холодные данные на разные таблицы, и вам нужно часто выполнять крупномасштабное удаление, то, к сожалению, DynamoDB совсем не подходит для вас. Это может стать дороже или очень медленно (зависит от вашего бюджета). В этих случаях я рекомендую найти другую базу данных для ваших данных.

Иман Седиги
источник
0

Мой подход к удалению всех строк из таблицы i DynamoDb заключается в том, чтобы просто вытащить все строки из таблицы с помощью DynamoDbs ScanAsync, а затем передать список результатов в DynamoDbs AddDeleteItems. Код ниже на C # у меня отлично работает.

        public async Task DeleteAllReadModelEntitiesInTable()
    {
        List<ReadModelEntity> readModels;

        var conditions = new List<ScanCondition>();
        readModels = await _context.ScanAsync<ReadModelEntity>(conditions).GetRemainingAsync();

        var batchWork = _context.CreateBatchWrite<ReadModelEntity>();
        batchWork.AddDeleteItems(readModels);
        await batchWork.ExecuteAsync();
    }

Примечание. Удаление таблицы с последующим ее воссозданием из веб-консоли может вызвать проблемы при использовании YAML / CloudFront для создания таблицы.

Мохаммад
источник
0

У нас нет возможности обрезать динамо-таблицы. мы должны отбросить таблицу и снова создать. Сборы DynamoDB основаны на ReadCapacityUnits и WriteCapacityUnits. Если мы удалим все элементы с помощью функции BatchWriteItem, он будет использовать WriteCapacityUnits, поэтому лучше удалить определенные записи или удалить таблицу и начать заново.

Шраван Хеббар
источник