Гарантии согласованности
Три настройки управляют согласованностью и долговременностью в RethinkDB: подтверждения записей и долговременность на уровне таблицы, и режим чтения запросов.
Настройки
-
Подтверждения записей устанавливаются на уровне таблицы с помощью настройки
write_acks, либо с помощью команды config, либо путем записи вtable_configсистемную таблицу. По умолчанию установлено значениеmajority, что означает, что записи будут подтверждены, когда большинство (голосовых) реплик подтвердят свои записи. Другая возможная опция —single, означающая, что записи будут подтверждены после подтверждения одной репликой. -
Долговременность устанавливается на уровне таблицы с помощью настройки
durability, опять же, используяreconfigureили запись вtable_configсистемную таблицу. В режиме долговременностиhard, записи сохраняются на диск перед отправкой подтверждений; в режимеsoft, записи подтверждаются сразу после хранения в памяти. Режимsoftбыстрее, но немного менее устойчив к сбоям. По умолчанию установлено значениеhard. -
Режим чтения устанавливается для каждого запроса через необязательный аргумент,
read_mode(илиreadMode), к таблице. У него есть три возможных значения:-
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 могут увидеть её, но когда проблема, вызвавшая сбой сети или сервера, будет устранена, запись может быть или не быть отменена. Как правило, записи завершаются неопределённо неудачно, если они выполнялись в момент возникновения сбоя сети или сервера. Оба эти сбоя приведут к ошибке, и вы можете проверить сообщение об ошибке, чтобы увидеть, была ли ошибка определённой или неопределённой.
Автоматическое переключение RethinkDB имеет ограничения в случаях сбоя непереходной связности, т. е., сервер A может связаться с B, а B — с C, но A не может связаться с C. Подробнее см. документацию по Переключению.
© RethinkDB contributors
Licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License.
https://rethinkdb.com/docs/consistency/