Читаем Создание микросервисов полностью

Мы уже говорили о некоторых простых технологиях масштабирования баз данных. Возьмем одну из них, чтобы исследовать идеи, положенные в основу теоремы CAP. Представим, что сервис инвентаризации развернут на базе двух отдельных дата-центров (рис. 11.8). В каждом дата-центре экземпляры сервиса поддерживает база данных, и эти две базы данных обмениваются данными, стараясь синхронизировать их между собой. Операции чтения и записи осуществляются через локальный узел базы данных, а для синхронизации данных между узлами применяется репликация.

Теперь подумаем о том, что случится, когда что-нибудь откажет. Представим, что прекратило работу что-либо настолько простое, как сетевая связь между двумя дата-центрами. С этого момента синхронизация даст сбой. Записи, осуществляемые в основную базу данных в дата-центре DC1, не будут дублироваться на дата-центр DC2, и наоборот. Большинство баз данных, поддерживающих такие настройки, поддерживают также какую-либо разновидность технологии выстраивания очередей, чтобы впоследствии обеспечить возможность восстановления из этой ситуации. Но что произойдет до такого восстановления?

Рис. 11.8. Использование репликации двух основных баз для распределения данных между двумя узлами баз данных

Принесение в жертву согласованности

Предположим, что сервис инвентаризации нами полностью не отключен. Теперь при внесении изменений в данные в DC1 база данных в DC2 их не видит. Это означает, что любой запрос, обращенный к узлу инвентаризации в DC2, увидит потенциально устаревшие данные. Иными словами, система все еще доступна на обоих узлах, способных обслуживать запросы, и мы сохранили работоспособность системы, несмотря на разделение, но утратили согласованность. Зачастую это называется AP-системой. Мы не можем сохранить все три свойства.

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

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

Принесение в жертву доступности

А что, если потребуется сохранить согласованность и отказаться вместо нее от чего-то другого? Итак, для сохранения согласованности каждый узел базы данных должен знать, что у него имеется такая же копия данных, как и у других узлов баз данных. Теперь при разделении, если узлы баз данных не могут связываться друг с другом, они не могут выполнять координацию для обеспечения согласованности. Мы не в состоянии гарантировать согласованность, поэтому единственным вариантом остается отказ от ответа на запрос. Иными словами, мы приносим в жертву доступность. Система согласована и терпима к разделению, или же можно сказать, что она приняла форму CP. В этом режиме сервису придется выработать меры снижения уровня функциональности до тех пор, пока не будет преодолено разделение и узлы баз данных не восстановят синхронизированность.

Обеспечить согласованность нескольких узлов довольно трудно. В распределенных системах сложно найти задачу труднее этой (если таковая вообще сможет найтись). Давайте немного порассуждаем на эту тему. Представим, что нужно прочитать запись из локального узла базы данных. Как узнать, что эта запись не устарела? Нужно обратиться с вопросом к другому узлу. Но мне также нужно попросить узел базы данных не разрешать обновление этой записи до тех пор, пока чтение не будет завершено, иными словами, для обеспечения согласованности нужно инициировать транзакционное чтение между несколькими узлами баз данных. Но люди, как правило, не связываются с транзакционным чтением, не так ли? Оно слишком медленное. Они запрашивают блокировки. Чтение может заблокировать всю систему. Для выполнения этой задачи всем согласованным системам требуется определенный уровень блокировки.

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

Перейти на страницу:

Похожие книги