Гарантии согласованности
Три настройки контролируют согласованность и долговечность в RethinkDB: подтверждения записи и долговечность на уровне таблицы, а также режим чтения запросов.
Настройки
-
Подтверждения записи устанавливаются на уровне таблицы с помощью настройки
write_acks, либо используя команду config, либо записывая вtable_configсистеменную таблицу. По умолчанию установлено значениеmajority, означающее, что записи будут подтверждаться, когда большинство (голосовых) реплик подтвердят свои записи. Другой возможный вариант —single, означающий, что записи будут подтверждаться, когда это подтвердит одна реплика. -
Долговечность устанавливается на уровне таблицы с помощью настройки
durability, снова используя либоreconfigure, либо записывая вtable_configсистемную таблицу. В режиме долговечностиhard, записи сохраняются на диск до отправки подтверждения; в режимеsoft, записи подтверждаются немедленно после сохранения в памяти. Режимsoftбыстрее, но немного менее устойчив к отказам. По умолчанию установлено значениеhard. -
Режим чтения устанавливается для каждого запроса через необязательный аргумент
read_mode(илиreadMode) для table. Он имеет три возможных значения:-
singleвозвращает значения, находящиеся в памяти (но необязательно записанные на диск) на первичной реплике. Это значение по умолчанию. -
majorityвозвращает только значения, которые надежно сохранены на диске на большинстве реплик. Это требует отправки сообщения каждой реплике при каждом чтении, поэтому это самый медленный, но наиболее согласованный режим. -
outdatedвозвращает значения, находящиеся в памяти на произвольно выбранной реплике. Это самый быстрый, но наименее согласованный режим.
-
Обратите внимание, что changefeeds игнорируют флаг read_mode, и всегда ведут себя так, как будто он установлен в single.
Линейная согласованность и гарантии атомарности
При следующих настройках RethinkDB гарантирует линейную согласованность отдельных атомарных операций над отдельными документами:
-
write_acks:majority -
durability:hard -
read_mode:majority
Это означает, что каждый запрос чтения увидит каждую предыдущую успешную запись, и ни один запрос чтения никогда не увидит окончательно проваленную запись. (См. примечание об окончательных провалах записи по сравнению с неопределенными записями ниже.)
Гарантия линейной согласованности относится к атомарным операциям, а не к запросам. Один запрос RethinkDB не обязательно выполняется как единая атомарная операция. Возможно, запрос:
r.table("foo").get("bar").eq(r.table("foo").get("bar")).run(conn, callback);
вернет false! Каждая отдельная операция get является атомарной, но запрос в целом не является атомарным. Чтобы прочитать и изменить документ в одной атомарной операции, используйте команды update или replace.
r.table("foo").get(id).update({hits: r.row("hits") + 1}).run(conn, callback);
Это также можно использовать для реализации регистров «проверка и установка». Следующий запрос атомарно проверит, равно ли поле check значению old_value, и изменит его на new_value, если это так:
r.table("foo").get(register_id).update({
check: r.branch(r.row("check").eq(old_value), new_value, r.row("check"))
}).run(conn, callback);
Операции RethinkDB никогда не являются атомарными по нескольким ключам. По этой причине RethinkDB нельзя считать базой данных ACID.
В настоящее время filter, get_all и аналогичные операции выполняются как отдельные операции от update и других операций изменения. Поэтому следующее не является правильной реализацией регистра «проверка и установка», поскольку filter и update не будут выполняться в одной атомарной операции:
r.table("foo").filter({
id: register_id, foo: old_val
}).update({foo: new_val}).run(conn, callback);
table.filter({id: register_id, foo: old_val}).update({foo: new_val})
Это поведение может измениться в будущем. Смотрите Github issue #3992, чтобы отследить обсуждение.
Гарантии доступности
За исключением кратких периодов, таблица останется полностью доступной, если более половины голосующих реплик для каждого фрагмента и для всей таблицы в целом доступны. Если потеряно половина или больше голосующих реплик для фрагмента, то операции чтения или записи для этого фрагмента завершатся ошибкой.
Переконфигурация таблицы (изменение количества фрагментов, перебалансировка и т. д.) приводит к кратковременной потере доступности в различные моменты переконфигурации.
Если первичная реплика потеряна, но более половины голосующих реплик всё ещё доступны, произвольная голосующая реплика будет избрана в качестве первичной. Новая первичная реплика появится в table_status, но поле primary_replica в table_config не изменится. Если старая первичная реплика когда-либо снова станет доступной, система вернётся к ней. При изменении первичной реплики наступит кратковременный период недоступности.
Если потеряна половина или больше голосующих реплик фрагмента, единственный способ восстановить доступность — запустить reconfigure с опцией emergency_repair. Обратитесь к документации по reconfigure для получения дополнительной информации.
Чтения в режиме single могут быть успешными даже если таблица недоступна, но это не гарантируется. Чтения в режиме outdated будут успешными, пока доступна хотя бы одна реплика для каждого соответствующего фрагмента.
Голосовые и не голосующие реплики? По умолчанию все реплики являются «голосовыми», что просто означает, что они учитываются в любой операции, для которой требуется большинство доступных реплик. Однако скорость, с которой реплики «голосуют», зависит от задержки сети; если у вас есть удалённый дата-центр с высокой задержкой, вы можете установить его реплики как не голосующие, чтобы улучшить производительность, за счёт гарантии доступности в этом дата-центре. Вы можете установить реплику как «не голосующую», изменив её конфигурацию таблицы с помощью
reconfigure.
Баланс безопасности и производительности
По умолчанию RethinkDB отдаёт приоритет безопасности над производительностью, за исключением одного случая: read_mode по умолчанию установлено в значение single а не majority. Режим чтения majority требует отправки запроса всем репликам и ожидания ответа от большинства из них, что существенно снижает производительность.
В нормальной работе режим чтения single даёт те же результаты, что и режим чтения majority, но он может возвращать устаревшие результаты в случае сбоя сети или краша. Также возможно, что чтение в режиме single может вернуть результаты от незавершенной записи, которая позже будет отменена.
То же самое относится к режиму записи single и режиму долговечности soft. В нормальной работе они дают те же результаты, что и majority и hard, но в случае сбоя сети или сервера недавние операции записи, выполненные в этих режимах, могут быть утеряны.
Обратите внимание, что write_acks и durability фактически не влияют на выполнение записи; они только влияют на время отправки подтверждения клиенту.
Чтения в режиме "outdated" вернут устаревшие данные даже при нормальной работе, но данные, как правило, будут устаревшими менее чем на секунду. В случае сбоя сети или сервера данные могут быть намного более устаревшими. Преимущество чтения в режиме "outdated" заключается в том, что задержка и пропускная способность часто лучше, чем в режиме "single", помимо различий в доступности, описанных в предыдущем разделе.
Примечания
Использование опции emergency_repair для таблицы аннулирует все гарантии.
Существует два способа, которыми операция записи может завершиться неудачей. Если запись завершается окончательно, ни один запрос чтения никогда её не увидит, даже в режимах чтения с более слабой гарантией. Если она завершается неопределённо, запросы чтения в режимах single или outdated могут её увидеть, но когда исправление сбоя сети или сервера, вызвавшего проблему, будет выполнено, запись может быть или не быть отменена. Как правило, записи завершаются неопределённо, если они выполнялись в точный момент возникновения сбоя сети или сервера. Оба эти вида отказов приведут к ошибкам, и вы можете изучить сообщение об ошибке, чтобы определить, был ли отказ окончательным или неопределённым.
Автоматический failover RethinkDB имеет ограничения в случаях сбоев связности, не имеющих транзитивности, т. е. сервер A может связаться с B, а B с C, но A не может связаться с C. Обратитесь к документации Failover для получения дополнительной информации.
© RethinkDB contributors
Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
https://rethinkdb.com/docs/consistency/