Синхронизация доступа к базе данных в распределенном приложении - PullRequest
5 голосов
/ 31 января 2011

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

Let X = some value
Let Database = some external Database handle

if !Database.contains(X):
   SomeCalculation()
   Database.insert(X)

Однако в многопоточной программе у нас есть условие гонки. Поток A может проверить, находится ли X в Database, обнаружить, что это не так, и затем перейти к вызову SomeCalculation(). Тем временем поток B также проверит, находится ли X в Database, обнаружит, что это не так, и вставит дублирующую запись.

Так что, конечно, это нужно синхронизировать как:

Let X = some value
Let Database = some external Database handle

LockMutex()
if !Database.contains(X):
   SomeCalculation()
   Database.insert(X)
UnlockMutex()

Это нормально, за исключением того, что если приложение является распределенным приложением, работающим на нескольких компьютерах, каждый из которых взаимодействует с одним и тем же внутренним компьютером базы данных? В этом случае Mutex бесполезен, потому что он синхронизирует только один экземпляр приложения с другими локальными потоками. Чтобы это работало, нам понадобится некая «глобальная» техника распределенной синхронизации. (Предположим, что просто запретить дубликаты в Database - неосуществимая стратегия.)

В целом, каковы некоторые практические решения этой проблемы?

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

Я намеренно избегал указывать, говорю ли я о СУБД или базе данных SQL, а не о чем-то вроде базы данных NoSQL, потому что снова - я ищу обобщенных ответов , основанных на отраслевых практиках. Например, может ли эта ситуация разрешить хранимые процедуры Atomic? Или атомарные транзакции? Или это что-то вроде «распределенного мьютекса»? Или, в более общем плане, эта проблема обычно решается системой базы данных, или она должна решаться самим приложением?

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

Ответы [ 3 ]

2 голосов
/ 31 января 2011

Одним из надежных способов защиты от смещения данных является блокировка строки данных.Многие базы данных позволяют вам делать это с помощью транзакций.Некоторые не поддерживают транзакции.

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

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

Приложение, которое изменяет данные, сначала читает строку, а затем выполняет все необходимые вычисления, а затем в какой-то момент записывает обновленные данные обратно в хранилище данных.Через оптимистический параллелизм приложение записывает обновление с условием (выраженным в SQL, если это база данных SQL), что строка данных должна обновляться только в том случае, если подпись не изменилась за это время.И каждый раз, когда строка данных обновляется, подпись также должна обновляться.

Результатом является то, что обновления не будут выбиты.Но для более строгого объяснения проблем параллелизма обратитесь к этой статье об уровнях изоляции БД.

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

0 голосов
/ 31 января 2011

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

Let X = some value
Let Database = some external Database handle

xResposible = Definer.defineResponsibleFor(X);

if(xResposible == me)
    if !Database.contains(X):
       SomeCalculation()
       Database.insert(X)

Хитрость в том, чтобы сделать defineResponsibleFor всегда возвращать одно и то же значение независимо от того, кто звонит. Таким образом, если у вас есть справедливый распределенный диапазон X и хороший определитель, все машины будут работать. И вы могли бы использовать простой многопоточный мьютекс, чтобы избежать условий гонки. Конечно, теперь вы должны позаботиться о отказоустойчивости (если машина или процесс не работают, ваш определитель должен знать и не определять для них какую-либо работу). Но ты должен сделать это в любом случае ...:)

0 голосов
/ 31 января 2011

Очевидно, что вы можете переместить «синхронизирующую» часть на сам уровень БД, используя эксклюзивную блокировку для определенного ресурса.

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

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