Баз данных, Дизайн программного обеспечения
     

Возможная согласованность, точность и повторяемость синхронизации данных

Создание распределенных, масштабируемых систем является сложным — и, что еще более распространено благодаря растущему числу IoT и мобильных устройств. Проблема, которую я обычно вижу, это попытки разработчиков рассматривать распределенные системы как монолитные системы. В частности, разработчики, которые держатся за шаблоны, такие как ACID.

ACID, сокращение от атома, согласованности, изоляции и долговечности, представляет собой основной столп реляционных баз данных (RDMS). Для многих приложений поведение, описанное ACID и принудительное RDMS, является именно тем, что желательно. Никто не хочет таких условий гонки, как грязные читает. Тем не менее, вы читаете это, потому что вы предположительно заинтересованы в распределенных системах — короче говоря, вы не строите типичное приложение, и, конечно, не типы приложений, предусмотренных в 1960-х годах, когда Codd и т.д. al. заложил основу для современных RDMS.

picture of Saturn, God of time
Может быть, Сатурн, бог времени, знает несколько вещей о возможной последовательности?

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

Очевидно, что вы не строите то, что «всегда связано», и поэтому строгая ACIDity не может быть достигнута. Что же тогда?

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

«Я хочу убедиться, что любые обновления обрабатываются правильно, и если возникает конфликт, что приложение делает правильные вещи.»

Ладно, достаточно хорошо. Эти требования четко определяют точку зрения пользователя, но, очевидно, оставляют немного места для интерпретации — мы будем работать с этим и оценивать двусмысленности, как они возникают.

Требование 1: «обеспечить корректную обработку любых обновлений».

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

Требование 2: «если возникает конфликт, делайте правильные вещи»

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

Несколько обновлений

Если несколько клиентов пытаются обновить один и тот же рекорд, что вы делаете? Что делать, если они обновляют одно и то же поле или, возможно, различные поля для одной и той же записи?

Выведенные/рассчитанные поля

Многие приложения отказаться от строгой схемы базы данных нормализации для оптимизации производительности, а иногда это принимает форму вычислительных полей. Сумма детских записей, состояние объекта (например, состояние-машина) и т.д. Что происходит, когда противоречивые обновления (см. выше) также влияют на то, что в противном случае является вычисленным полем?

Удаления

В идеале следует избегать жестких удалений и реализовывать какую-либо форму логического удаления, но как эти шаблоны удерживайте конфликты?

Как мы будем действовать?

С предыдущей разбивкой типов конфликтов в сторону, давайте рассмотрим, как мы можем преодолеть это в отключенной системе.

К счастью, простое решение существует из-за сходства каждого из этих типов конфликтов — и, если уж на то пошло, всех конфликтов. Все конфликты по существу являются ошибками во времени. Вещь А должна была предшествовать вещи B, но из-за жизни, Вселенной и всего, что мы получаем до А. Ты знаешь это, потому что ты человек. Вы можете визуализировать эти сценарии.

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

  1. Фред измеряет хранение принять до ежедневной отгрузки, и записал ежедневные измерения на 47 литров. Фред, однако, находится в автономном режиме в течение этого времени из-за отсутствия надлежащего подключения к Интернету в хранилище.
  2. Алиса, сидя в офисе, обновление уровня жидкости же резервуар для хранения от предыдущих дней чтения 50 литров до 150 литров, потому что она только что получила уведомление от полевого техника, что их ежедневная доставка была только что получена и водитель доставки уведомил Алису о правильном чтении после долива покинуть бак.
  3. Позже в тот же день, Фред, наконец, получает подключен к Интернету, и его ранее записанные показания могут быть переданы.

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

Это потому, что обновления Фреда произошло хронологически, прежде чем те, записанные Алисой, и в этом заключается наше решение. Наша система должна сохранять и уважать хронологический порядок, что обновления должны были произойти, не обязательно, когда они были получены центральным сервером. В самом деле, время, когда обновление получено на сервере больше технического артефакта, хотя и любопытно, но, конечно, не представляет интереса для средних пользователей системы.

Чтобы исправить это, мы должны добавить еще одно поле для каждой модели, которую мы намерены разрешить в автономном режиме обновления. Это поле должно отслеживать точную таймштампу, которую пользователь указал, что он хочет, чтобы произошло действие. Назовем это «датой представления».

Возвращаясь к нашему вышеуказанного примера, дата представления обновления Фреда может быть 9 утра 1 ноября, в то время как Алиса, возможно, было 11:45 утра в тот же день. Если бы мы захватили эти метки времени, когда запись Фреда прибывает, мы можем ясно видеть, что он представляет значение, которое было предназначено, чтобы прибыть гораздо раньше. В самом деле, мы даже можем видеть, что у нас есть новые записи, и может выбрать, чтобы игнорировать его обновление.

На данный момент вы можете увидеть важность этого дополнительного таймштамп. Наша система начисляет много timestamp, мы получили обновленный, и создал даты, чтобы помочь облегчить синхронизацию вниз, и теперь мы добавили дату представления для облегчения до синхронизации. Это просто потому, что во время всех типов синхронизации, самая большая проблема заключается в том, что хронологическое несоответствие — ошибки синхронизации. Мы можем предотвратить это, поддерживая хорошую бухгалтерию, и так как предметом нашей озабоченности является время, то это означает, что мы просто сохраняя много тайм-tamps в качестве бухгалтерии, чтобы обеспечить надлежащий поток данных.

Как насчет удалений?

Мы уже установили основы для обработки удалений. Используя нашу недавно добавленную дату отправки timestamp, теперь мы можем легко игнорировать обновления, которые происходят для удаления записей … если время правильное. Видите ли, несмотря на то, что мы добавили новый таймштамп, мы затушевыли еще один вопрос времени. Удаления приносят их фронт-и-центр, потому что удаления на самом деле логические удаления (вы не трудно удаления записей, не так ли?), И как таковой включать обновление столбца.

На самом деле, конфликт удаления, возникающий до/после другого обновления, является просто особым случаем двух обновлений, происходящих, но на разных полях одной и той же модели.

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

  1. Фред посещает место хранения 4, и записи ежедневного давления чтения для хранения бак B — скажем, 60 PSI. Фред находится в автономном режиме, однако, так что его обновления не будут переданы еще.
  2. Алиса получает звонок, информирующий ее о получении дополнительных жидкостей для танка B на сайте 4. Она находится в сети, и сразу же обновляет показания жидкости до 150 литров.
  3. Фред, наконец, получает интернет-проводимости, и передает свои показания давления от ранее в тот же день.

Многое из этого сводится к тому, как мы структурировать наши данные, давайте сделать это еще более конкретным. Предполагая, что наша схема выглядит так:

К тому времени, когда мы приходим к шагу 3, у нас будут записи, которые выглядят следующим образом:

Если бы наша система, замевореняя время, просто проигнорировала представление Фреда, то мы бы просто выбросили данные, так как только запись Фреда содержит показания давления. Что делать?

Значение атрибута сущности для спасения

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

По своей сути, EAV говорит, что (E) Сущности (так что представление чтения для жидкости бак), которые содержат несколько (A) Атрибуты (уровень жидкости и показания давления нашего танка) должны быть представлены отдельные (V) Значения при записи. Короче говоря, мы должны пересмотреть нашу схему, что одна запись говорит: «уровень жидкости составляет 150 литров», а другой говорит: «давление 60 пси», но ни говорит, как. Путать? Давайте посмотрим, как выше схема может выглядеть под EAV и идти оттуда:

В этой пересмотренной схеме мы используем поле «чтение» для двойной цели. Иногда чтение давление, иногда это объем. Как дифференцировать два? Для этого и заключается поле типа чтения. В этом примере тип 1 представляет показания давления, а тип 2 представляет уровень жидкости. Эти типы полностью специфичны для приложений, так что не зацикливайтесь на этом, а наблюдайте, как в этой пересмотренной схеме мы по существу поворачивали нашу запись, и позволяйте сохранить обновление Алисы и Фреда без какого-либо конфликта, что-так-все.

В самом деле, если мы используем эту схему и цикл обратно в нашем предыдущем примере Фред также записи уровня жидкости, мы получаем что-то очень интересное:

 

Здесь мы не только использовали нашу новую схему, но и позволили хранить в базе данных более ранние показания давления Фреда. Так как мы используем как EAV шаблон, и имеют надлежащее submission_date timestamp, когда мы запрашиваем для правильного / текущего чтения жидкости, мы можем ясно видеть, что фред жидкости чтения произошло раньше, и игнорировать его. Тем не менее, мы все еще записали его для целей аудита. Кроме того, и, возможно, что более важно, это проще. Всякий раз, когда мы селективного обновления данных мы рискуем ошибок. Однако, если мы напишем все в то, что по существу является гигантским журналом аудита, мы можем (в момент запроса) разрешать конфликты в режиме реального времени — каждая недавно сохраненная запись ретроактивно разрешает все предыдущие конфликты.

Это последний бит стоит обсудить немного больше.

Хотя наши примеры до сих пор были довольно упрощенными, обновления времени может получить довольно сложно. Roll кости достаточно, и вы можете иметь несколько обновлений от одного и того же человека, некоторые онлайн, некоторые в автономном режиме, другие обновления от разных пользователей и т.д., все происходит параллельно. Вы можете, на каждой недавно полученной записи оценить все существующие записи в системе, и определить, если вы должны вставить или обновить эту строку, но что произойдет, если, как вы оценки, если обновление Алисы должны быть записаны, другое обновление от другого пользователя (скажем, Боб) прибывает. Если вы работаете несколько веб-серверов, как и почти любое современное веб-приложение, то логика, которая оценивает, если запись Алисы должны быть сохранены работает одновременно с Бобом, и оба не знают о других — больше конфликтов!

Вместо этого, выше шаблон позволяет как Алиса и Боб обновление будет написано в базу данных, и когда кто-либо читает данные, используется только запись с последней времени (как определено датой представления) используется.

 

About Jason

Джейсон является опытным предпринимателем и разработчиком программного обеспечения, квалифицированным в области лидерства, мобильной разработки, синхронизации данных и архитектуры SaaS. Он получил степень бакалавра наук (B.S.) в области компьютерных наук в Университете штата Арканзас.
View all posts by Jason →

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *