diff --git a/materials/09-data-replication/09-data-replication.pdf b/materials/09-data-replication/09-data-replication.pdf new file mode 100644 index 0000000..2e4b55e Binary files /dev/null and b/materials/09-data-replication/09-data-replication.pdf differ diff --git a/materials/09-data-replication/readme.md b/materials/09-data-replication/readme.md new file mode 100644 index 0000000..cf25275 --- /dev/null +++ b/materials/09-data-replication/readme.md @@ -0,0 +1,10 @@ +## 9. Репликация данных. + +### Лекция + +- [Презентация](09-data-replication.pdf) +- См. также материалы в конце + +### Семинар + +- [Материалы семинара](seminar/readme.md) \ No newline at end of file diff --git a/materials/09-data-replication/seminar/media/async-lost.png b/materials/09-data-replication/seminar/media/async-lost.png new file mode 100644 index 0000000..aacf6c4 Binary files /dev/null and b/materials/09-data-replication/seminar/media/async-lost.png differ diff --git a/materials/09-data-replication/seminar/media/dynamo-replication.svg b/materials/09-data-replication/seminar/media/dynamo-replication.svg new file mode 100644 index 0000000..cdbecc4 --- /dev/null +++ b/materials/09-data-replication/seminar/media/dynamo-replication.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/materials/09-data-replication/seminar/media/lrc.png b/materials/09-data-replication/seminar/media/lrc.png new file mode 100644 index 0000000..1eac2d1 Binary files /dev/null and b/materials/09-data-replication/seminar/media/lrc.png differ diff --git a/materials/09-data-replication/seminar/media/raid-0.png b/materials/09-data-replication/seminar/media/raid-0.png new file mode 100644 index 0000000..21ca62e Binary files /dev/null and b/materials/09-data-replication/seminar/media/raid-0.png differ diff --git a/materials/09-data-replication/seminar/media/raid-1.png b/materials/09-data-replication/seminar/media/raid-1.png new file mode 100644 index 0000000..41bb9ac Binary files /dev/null and b/materials/09-data-replication/seminar/media/raid-1.png differ diff --git a/materials/09-data-replication/seminar/media/raid-6.png b/materials/09-data-replication/seminar/media/raid-6.png new file mode 100644 index 0000000..3a32e5a Binary files /dev/null and b/materials/09-data-replication/seminar/media/raid-6.png differ diff --git a/materials/09-data-replication/seminar/media/sync-async.png b/materials/09-data-replication/seminar/media/sync-async.png new file mode 100644 index 0000000..230a968 Binary files /dev/null and b/materials/09-data-replication/seminar/media/sync-async.png differ diff --git a/materials/09-data-replication/seminar/media/sync-replication.png b/materials/09-data-replication/seminar/media/sync-replication.png new file mode 100644 index 0000000..ba73a5b Binary files /dev/null and b/materials/09-data-replication/seminar/media/sync-replication.png differ diff --git a/materials/09-data-replication/seminar/media/wal.svg b/materials/09-data-replication/seminar/media/wal.svg new file mode 100644 index 0000000..30cbd4b --- /dev/null +++ b/materials/09-data-replication/seminar/media/wal.svg @@ -0,0 +1,131 @@ + \ No newline at end of file diff --git a/materials/09-data-replication/seminar/readme.md b/materials/09-data-replication/seminar/readme.md new file mode 100644 index 0000000..196156d --- /dev/null +++ b/materials/09-data-replication/seminar/readme.md @@ -0,0 +1,239 @@ +# Семинар 9: Репликация + +В рамках данного семинара предлагается разобраться с концепцией репликации данных. +Идея простая — иногда вместо одного сервера необходимо завести его "реплики" (или копии), +где будет лежать одна и та же информация. + +Это позволяет добиться: +- Отказоустойчивость и сохранность данных — при отказе одного из серверов, данные можно получить с другого сервера. +- Увеличение производительности — если один сервер не справляется с обработкой всех запросов на чтение. +- Уменьшение latency — расположить сервера-реплики ближе к клиентам (пример — CDN). + +## Репликация в СУБД + +Как правило в существующих хранилищах (СУБД) репликация уже реализована и используется повсеместно. + +Как правило выделяется узел, который принимает запросы от пользователя на запись. Далее этот узел должен +отправить запрос на запись в остальные узлы (реплики). + +### Sync and async replication + +Подход, когда есть один узел, принимающий запросы на запись и реплицирующий данные на другие реплики, +называется leader-follower (ранее известный как master-slave). + + + +*[Источник](https://arpitbhayani.me/blogs/replication-strategies/)* + +Выделяют два вида репликации по принципу "когда клиент получает ответ на запрос записи": +- **Синхронная репликация** — данные сразу дублируются на все реплики, и только после + подтверждения записи на других репликах, клиент получает ответ об успешной записи. + Это позволяет избежать потери данных при отказе узла, который производит запись, но сложнее + в реализации и увеличивает latency ответа клиенту. +- **Асинхронная репликация** — клиент получает ответ сразу после успешной записи на узел, который + изначально принял запрос пользователя. Данные на реплики дублируются в фоновой операции. + +Возможна комбинация данных режимов: например, одна sync-реплика и несколько async-реплик: + + +*Источник: Martin Kleppmann, Designing Data-Intensive Applications* + +Асинхронный режим репликации проще в реализации, уменьшает latency ответа клиенту, +но может привести к потере данных (лидер не успел отправить всю информацию на реплики). + +Также в async-репликации возможны так называемые stale чтения (чтение возвращает не самую актуальную информацию): + + +*Источник: Martin Kleppmann, Designing Data-Intensive Applications* + +### Что реплицировать + +Возникает вопрос, а что именно необходимо реплицировать. + +**Репликация запросов**. Лидер, приняв запрос от клиента, выполняет аналогичный запрос на всех репликах, +а затем возвращает ответ клиенту. Вариант прост для реализации и довольно экономный, однако возможны трудности +с недетерминированными запросами. Если запрос полагается на вызов rand(), это значение на каждом из +узлов может быть разным, что приведет к расхождению данных. + +**Row-based репликация**. Если представить, что наша модель данных может быть представлена в виде таблицы +(например, таблицы из пар ключ-значение), то можно реплицировать все изменения строк в данной таблице. +Таким образом мы не будем страдать от недетерминированных запросов, так как предварительно лидер применяет +все изменения к своему состоянию и только потом отправляет измененные значения строк. Однако в случае +массовых операций обновления (PUT *=NULL) потребуется передавать довольно много информации. + +**Репликация файловой системы**. Можно реплицировать файловую систему. При вызове системного вызова +write(offset, "data") этот запрос проксируется на все реплики, и каждая из них выполняет запись в +свою локальную файловую систему. Реализация возможна на уровне операционной системы, однако в таком +подходе необходимо следить за совместимостью версий операционной системы и файловой системы, что не всегда +возможно. + +Идеального подхода к репликации не существует; необходимо выбирать его из решаемой задачи и имеющихся +ограничений. + +В современных СУБД для восстановления и репликации часто используют WAL (write-ahead log) — журнал +предзаписи. При изменении данных СУБД формирует запись WAL и обновляет страницу в оперативной памяти; +страница может измениться в памяти раньше, чем журнал попадёт на диск. Правило write-ahead требует сохранить WAL +на диске до того, как туда будет записана изменённая страница. Если после сбоя страница на диске оказалась +устаревшей, СУБД может воспроизвести журнал и восстановить недостающие изменения. + +Для репликации лидер передаёт записи WAL реплике, а та применяет их у себя в том же порядке. При физической +репликации узлам нужны совместимые форматы страниц и WAL. + + + +*[Источник](https://martinfowler.com/articles/patterns-of-distributed-systems/write-ahead-log.html)* + +### Кворумная репликация + +Дожидаться подтверждения операции от всех реплик может быть накладно, однако хочется недопустить потери значения. +В таких условиях возникла идмея кворумной репликации. + +[Кворум](https://en.wikipedia.org/wiki/Quorum_(distributed_computing)) — это минимальное +количество подтверждений, которое необходимо получить для того, чтобы считать операцию выполненной. + +Допустим, мы хотим реплицировать данные на три реплики. При получении запроса будем отправлять запросы +на запись на все три реплики, но дожидаться ответа будем только от двух реплик (от тех, что выполнили +операцию быстрее). Если ответ получен от двух реплик, то считаем операцию выполненной. + +Внимательный слушатель заметит, что при такой реализации запрос на чтение может вернуть неактуальные данные, +если одна из реплик еще не успела применить обновления. Чтобы решить эту проблему, предлагается +отправлять запросы на чтение на все реплики, дожидаться ответа от двух самых быстрых реплик и выбирать +наиболее актуальную версию значения из полученных (тему версионирования затронем далее в курсе). + +В таком случае нам неважно, какие реплики подтвердили запись и ответили на операцию чтения. +Так как на операцию записи и чтения мы обращались на две реплики, как бы мы не выбрали эти множества +реплик, они обязательно пересекутся как минимум по одной реплике. Эта реплика будет содержать +наиболее актуальные данные. + +Если обобщить задачу на произвольное число реплик N, то для кворума необходимо выбрать числа R и W. +R — число реплик, от которых мы ждем ответа на операцию чтения, W — число реплик, +которые должны подтвердить операцию записи. Чтобы система возвращала актуальные значения, должно +выполняться условие `R + W > N`. В зависимости от отношения и сложности операций чтения и записи +можно выбирать наиболее подходящие R и W для вашей системы. + +Реализацию этой идеи можно увидеть в СУБД Cassandra +(и почитать в paper'e о Dynamo, секция 4, sloppy quorum & hinted handoff). + + + +*[Источник](https://www.designgurus.io/course-play/grokking-the-advanced-system-design-interview/doc/replication)* + +В [учебном примере KV](../../08-scaling/seminar/kv/) есть режим `replication-quorum`. +Прокси последовательно обращается ко всем узлам. Запись успешна, если её подтвердило большинство узлов. +При чтении прокси возвращает значение, которое совпало в ответах большинства. Например, для трёх узлов +ответы `v₂, v₂, v₁` дают `v₂`, а три разных значения приводят к ошибке. + +Этот пример показывает голосование по значениям. Для выбора самой свежей версии, описанного выше, +понадобятся метки версий и другое правило обработки ответов. + +## RAID + +[RAID](https://en.wikipedia.org/wiki/Standard_RAID_levels) (redundant array of inexpensive disks) +— переносит идею репликации данных из распределенной системы на уровень одного компьютера. + +Оказывается, к одному серверу можно подключить сразу несколько дисков с целью увеличения скорости доступа +и надежности сохранности (пережить отказ диска). + +Первая простая идея — поставить два диска рядом, писать половину данных на первый диск, +вторую половину — на второй. Надежность не увеличивается, но зато увеличивается вместительность +и скорость доступа. Такой подход называется RAID 0: + +
+
+
+
+
+
+
+
+