Обновление ключей базы данных, когда ключи одной таблицы относятся к ключам другой - PullRequest
0 голосов
/ 28 июня 2018

У меня есть две таблицы в DynamoDB. У одного есть данные о домах, у одного есть данные о бизнесе. Стол домов имеет список ближайших к нему предприятий, с указанием времени ходьбы до каждого из них. То есть таблица home содержит список идентификаторов, которые ссылаются на элементы в таблице business. Поскольку предприятия постоянно открываются и закрываются, обе эти таблицы требуют частого обновления.

Проблема, с которой я сталкиваюсь, заключается в том, что при обновлении одной из таблиц в другой таблице будут неверные данные, пока она не обновится сама. Чтобы было понятнее: скажем, один бизнес закрывается, а другой открывается. Я мог бы сначала обновить таблицу предприятий, чтобы удалить старую компанию и добавить новую, но тогда таблица домов все равно будет ссылаться на уже удаленную компанию. Точно так же, если бы я сначала обновил таблицу домов, ссылаясь на новый бизнес, то в таблице предприятий еще не было бы данных этого нового бизнеса. Независимо от того, какую таблицу я обновляю первой, всегда будет период времени, когда две таблицы не синхронизируются.

Какой лучший способ справиться с этой проблемой? Один из способов, который я рассмотрел, - это сделать все обновления для вторичной базы данных, а затем поменять ее местами с моей первичной базой данных, но мне интересно, есть ли лучший способ.

Спасибо!

1 Ответ

0 голосов
/ 03 июля 2018

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

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

  1. Удалить Business(id=123) из таблицы.
  2. Обновление Home(id=456) для удаления связи с Business(id=123) из массива home.businesses.

Вот что вы можете сделать, чтобы имитировать транзакцию :

Создание отметки времени для блокировки элементов

Допустим, наша текущая временная метка 1234567890. Использование метки времени позволит вам очистить неудачные транзакции (я объясню позже).

Блокировка двух предметов

Обновите Business-123 и Home-456 и установите атрибут lock=1234567890.

Не изменяйте никакие другие атрибуты в этой операции обновления!

Используйте ConditionalExpression (см. Руководство разработчика и API ), чтобы проверить, что attribute_not_exists(lock) перед обновлением. Таким образом, вы уверены, что нет другого процесса, использующего те же предметы.

Обрабатывать ответы блокировки обновления

Проверьте, успешно ли оба обновления для дома и бизнеса. Если да для обоих, это означает, что вы можете приступить к фактическим изменениям, которые необходимо внести: удалите Business-123 и обновите Home-456, удалив Business Association.

Для дополнительной осторожности также используйте ConditionExpression в обоих обновлениях снова, но теперь убедитесь, что lock == 1234567890. Таким образом, вы уверены, что никакой другой процесс не перезаписал вашу блокировку.

Если оба обновления снова пройдут успешно, вы можете считать эти два элемента обновленными и согласованными для чтения другими процессами. Для этого запустите третье обновление, удалив атрибут lock из обоих элементов.

При сбое одной из операций вы можете, например, повторить попытку X раз. Если это не удалось все X раз, убедитесь, что процесс очищает другие lock, которые были выполнены ранее.

Обеспечить блокировку транзакции через ваш код

Всегда используйте ConditionExpression в любой части вашего кода, которая может обновлять / удалять элементы Home и Business. Это очень важно для того, чтобы решение работало.

При чтении предметов «Дом» и «Бизнес» вам необходимо это сделать ( это может не понадобиться при всех чтениях, вы решите, нужно ли вам обеспечивать согласованность от начала до конца при работе с прочитанным элементом из БД ):

  1. Получить элемент, который вы хотите прочитать
  2. Создать временную метку блокировки
  3. Обновить элемент с помощью lock=timestamp, используя ConditionExpression
  4. Если обновление выполнено успешно, продолжайте использовать элемент в обычном режиме; если нет, подождите одну или две секунды и повторите попытку;
  5. Когда вы закончите, обновите элемент, удалив lock

Регулярно очищать неудачные транзакции

Каждую минуту или около того запускайте фоновый процесс для поиска потенциально неудачных транзакций. Если вашим процессам требуется максимум 60 секунд, и есть элемент со значением lock старше, скажем, 5 минут (помните, что значение lock - это время начала транзакции), можно с уверенностью сказать, что эта транзакция в какой-то момент завершилась неудачно и какой бы процесс ни выполнялся, он не очищал блокировки должным образом.

Это фоновое задание гарантирует, что ни один элемент не будет заблокирован на вечность.

Остерегайтесь этой реализации, не обеспечивающей реальной атомарной и согласованной транзакции в том смысле, как это делают традиционные БД ACID. Если это важно для вас (например, вы имеете дело с финансовыми операциями), не пытайтесь реализовать это. Поскольку вы сказали, что с вами все в порядке, если атомное происшествие нарушается в редких случаях отказа, вы можете жить с этим счастливо. ;)

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...