Распределённые системы
Введение в распределённые системы
Цели урока
- Понимать определение распределённой системы по Лэмпорту и его практический смысл
- Знать четыре класса сбоев и стратегии защиты от каждого
- Применять CAP-теорему для выбора БД и архитектуры
- Различать уровни согласованности от linearizable до eventual
Предварительные знания
- Базовое понимание сетевых протоколов (TCP, HTTP)
- Опыт работы с одной БД (PostgreSQL/MySQL/любая SQL)
- Знание понятия транзакции (ACID)
AWS S3, 28 февраля 2017: одна опечатка одного инженера в одной команде положила половину интернета на 4 часа. 150 млн долларов потерь по индустрии. Это не баг - это природа распределённых систем.
- **Knight Capital (440 млн долларов за 45 минут)** - один забытый флаг feature toggle на одном из восьми серверов
- **Ariane 5 (370 млн долларов)** - integer overflow при конвертации в инерциальной системе SRI через 37 секунд после старта
- **Facebook BGP outage (6 часов, 2021)** - неправильная конфигурация маршрутизации отрезала FB от мира
- **Cloudflare Regex outage (2 июля 2019)** - один неэффективный regex положил CDN/WAF Cloudflare на 27 минут
- **Google Spanner** - атомные часы и GPS в каждом дата-центре чтобы обмануть CAP
Лесли Лэмпорт и рождение distributed computing
В 1978 году Лэмпорт опубликовал статью "Time, Clocks, and the Ordering of Events in a Distributed System" - 8 страниц, которые создали целую область. Идея простая: в распределённой системе нет глобального времени, но есть **causal ordering** через логические часы (Lamport timestamps). За эту работу и Paxos он получил Turing Award в 2013. Одна из самых цитируемых статей в распределённых системах (около 14 700 цитирований).
Что такое распределённая система
**28 февраля 2017. 9:37 утра PST. Один инженер Amazon вводит команду в S3 console - опечатка в параметре. За 4 часа из-за этой опечатки лежит половина интернета: Slack, Trello, Quora, Medium, Coursera. Потеряно 150 млн долларов бизнес-выручки за один тикет.** Это и есть распределённая система: миллион вращающихся шестерёнок, любая может остановить всё.
Распределённая система - это когда падение машины, о существовании которой никто не знал, мешает работать.
Лучшего определения за 38 лет не написали. Каждое слово - про реальную боль production-инженера: какая-то VM в дата-центре где-то в Айове, про которую никто не помнил, что от неё зависит auth pipeline - и теперь весь сайт возвращает 503. Независимый сбой и порождает потребность в консенсусе между репликами.
**Формально:** распределённая система - набор независимых компьютеров, которые для пользователя выглядят как единая когерентная система. **Практически:** код, где `network call != function call` - и это разрушает все привычные интуиции.
Масштаб настоящих распределённых систем
| Система | Узлы | Ключевая метрика |
|---|---|---|
| Bitcoin | ~17 000 full nodes | Без единого центра, byzantine-tolerant |
| AWS S3 | Миллионы дисков | 11 nines durability (потеря файла ~ выигрыш в лотерею 6 раз подряд) |
| Google Spanner | Тысячи узлов в 5 регионах | Глобальная strong consistency через атомные часы |
| Cloudflare DNS (1.1.1.1) | 330+ городов | p50 latency 11 мс глобально |
| Facebook Memcached | 10 000+ серверов | 1 миллиард запросов/сек |
Распределённая система = просто несколько копий приложения для масштабирования
Распределённая система = набор узлов с независимыми сбоями, частичными отказами и без общего времени
Главная проблема не в производительности, а в том, что каждый узел может упасть, ответить медленно или вернуть устаревшие данные - независимо от других. Один сервер либо работает, либо нет. Распределённая система всегда находится в каком-то промежуточном состоянии: 3 узла из 5 в норме, 1 отвечает с задержкой 2 сек, 1 вернул данные годовой давности.
Какое из утверждений ТОЧНЕЕ всего описывает распределённую систему по Лэмпорту?
Сбой - не исключение, а норма
**Knight Capital, 1 августа 2012. 9:30 EST. Запускают новый код на 8 серверах из 8 - но на одном забыли удалить старый флаг feature toggle. За 45 минут торгов сервер с глюком сделал 4 миллиона трейдов на 7 миллиардов долларов. К концу дня компания потеряла 440 миллионов долларов, через неделю потребовала экстренное вливание капитала, в декабре 2012 поглощена Getco. Один забытый файл на одном сервере из восьми.**
**Главное правило distributed systems:** вопрос не "упадёт ли узел", а "что делает система когда он упал". При 100 узлах с MTBF 1 год сбой происходит каждые 3.65 дня. При 1000 узлах - каждые 8.7 часов. При 10 000 - каждые 52 минуты. **Сбой - это нормальная операция, а не catastrophe.** Этот же blind spot перечислен в восьми заблуждениях.
Четыре класса сбоев (модель Cristian/Schneider)
| Тип сбоя | Что происходит | Защита |
|---|---|---|
| **CRASH** | Узел упал и больше не отвечает (kernel panic, OOM, kill) | Heartbeat + retry на другом узле кластера |
| **OMISSION** | Узел работает, но молча теряет сообщения (network drop, переполнение буфера) | Идемпотентность + acknowledgement |
| **TIMING** | Узел отвечает, но слишком поздно (GC pause, перегрузка CPU, swap) | Timeout + retry, circuit breaker |
| **BYZANTINE** | Узел врёт - искажает данные, шлёт противоречивые ответы (bug, hardware failure, malicious) | PBFT, Paxos с >2/3 здоровых узлов, signed messages |
Сбой омиссии в банковской транзакции
Сервер получил запрос на списание 1000 долларов. Списал. Отправил `200 OK`. Ответ потерялся в сети. Клиент видит timeout. Что делать клиенту? **Ретраить** - возможно двойное списание. **Не ретраить** - возможно списания не было. Решение: каждая транзакция получает уникальный `idempotency-key`. Сервер при повторном запросе с тем же ключом возвращает результат первого, не выполняя списание. Stripe и PayPal так работают с 2010-х.
Anything that can go wrong, will go wrong - and at scale, it will go wrong every Tuesday at 3 PM.
Если использовать надёжное оборудование, сбоев можно избежать
На масштабе тысяч узлов сбой - событие, происходящее каждые часы независимо от качества железа
Pinheiro/Weber/Barroso (Google, FAST 2007) опубликовали данные: AFR около 2% за первый год, 8% за второй, 8.6% за третий. На 100 000 дисков это тысячи отказов в год. Никакое железо не отменит статистику. Решение - проектировать систему так, чтобы сбой узла был routine event с автоматическим восстановлением.
При 1000 серверах с MTBF (mean time between failures) = 1 год, как часто в кластере что-то падает?
CAP-теорема - выбор из трёх
**Эрик Брюер, 2000, конференция PODC. Гипотеза:** распределённая система не может одновременно гарантировать три свойства - **C**onsistency, **A**vailability, **P**artition tolerance. **2002:** Сет Гилберт и Нэнси Линч из MIT доказали это формально. Теперь это закон, как термодинамика. Полный разбор - в уроке про CAP.
- **Consistency (C)** - все узлы видят одинаковые данные в один момент времени. Записал на одном - прочитал актуальное на любом другом.
- **Availability (A)** - каждый запрос получает ответ (успех или ошибка), без зависаний. Ни один live узел не молчит.
- **Partition tolerance (P)** - система продолжает работу при разрыве сети между узлами. В реальном мире сеть рвётся всегда, поэтому P не опция.
**Практическая трактовка:** P неизбежен (сети рвутся). Поэтому реальный выбор - **CP или AP**. Что приоритетнее когда узлы потеряли связь: точные данные ценой недоступности (CP) или какие-то данные ценой возможной устаревшести (AP).
Реальные системы и их выбор
| Система | Тип | Почему |
|---|---|---|
| PostgreSQL (single primary) | CP | Если primary недоступен - запись отвергается. Финансовые данные требуют точности. |
| MongoDB (с majority writes) | CP | При partition меньшая часть кластера становится read-only. |
| DynamoDB | AP | Eventual consistency по умолчанию - предпочитает доступность для shopping cart. |
| Cassandra | AP | Tunable consistency - можно выбрать на каждый запрос. |
| etcd / Consul (Raft) | CP | Координация кластера - лучше отказать, чем дать конфликтные конфиги. |
| DNS | AP | Обновление распространяется часами - устаревшие записи лучше, чем недоступный домен. |
**Распространённая ошибка:** считать CAP бинарным выбором на всю систему. На практике даже одна БД делает разный выбор для разных операций (Cassandra: `consistency_level` per query). Современный подход - **PACELC**: при Partition выбор между A и C, **Else** между Latency и Consistency.
Банк выбирает БД для хранения балансов счетов. Какой trade-off корректнее?
Спектр согласованности
**Strong consistency vs Eventual consistency** - это не два варианта, а полюса спектра. Между ними - десяток уровней, каждый с своим trade-off latency vs точность.
| Уровень | Гарантия | Latency | Пример |
|---|---|---|---|
| Linearizable | Запись видна всем мгновенно (как single machine) | Высокая (10-100мс глобально) | Google Spanner, etcd |
| Sequential | Все видят операции в одном порядке | Средняя | ZooKeeper read consistency |
| Causal | Связанные причинно операции в порядке | Низкая | Riak, COPS |
| Read-after-write (RYW) | Своя запись видна сразу, чужие eventually | Низкая | Session consistency в DynamoDB |
| Eventual | Когда-нибудь все узлы сойдутся | Минимальная | DynamoDB, Cassandra default |
Google Spanner: как обмануть CAP
Spanner - первая глобальная strong consistency БД. Ключ - **TrueTime API**: каждый дата-центр имеет атомные часы и GPS-приёмники. API возвращает не точное время, а интервал `[earliest, latest]` с гарантированной погрешностью (обычно 7мс). Транзакция ждёт пока интервал пройдёт - получает глобально упорядоченные timestamps без координации. Механика логического и физического времени разбирается в уроке про часы.
**Цена strong consistency:** каждый коммит блокируется на несколько мс ожидания. Пропускная способность Spanner - тысячи tx/sec на узел, а не миллионы как у DynamoDB. Поэтому Google использует Spanner для финансов и Ads, а для поиска - eventually consistent системы.
Главное из урока
- Распределённая система = независимые сбои + частичные отказы + нет общего времени (Lamport 1987)
- На масштабе тысяч узлов сбой происходит каждые часы - норма, а не исключение
- 4 класса сбоев: crash, omission, timing, byzantine - каждый требует своих защит
- CAP-теорема: P неизбежен, выбор между CP (точность) и AP (доступность)
- Consistency - спектр от linearizable до eventual, выбор на основе бизнес-требований
Что дальше в курсе
Каждая концепция этого урока разворачивается в отдельную тему.
- CAP-теорема и PACELC — Формальные доказательства и нюансы выбора
- Консенсус (Paxos, Raft) — Как узлы договариваются при сбоях
Ключевые понятия
- Распределённая система - это набор узлов, каждый из которых не знает о сбое другого (определение Лэмпорта)
- CAP-теорема: из трёх свойств (Consistency, Availability, Partition tolerance) реально выбирают два - и P всегда обязательно, значит выбор между C и A
- CP-системы (например, ZooKeeper, HBase) при разрыве сети отказывают запросам, но данные всегда согласованы
- AP-системы (например, Cassandra, DynamoDB) продолжают работать при разрыве, но разные узлы могут вернуть разные данные
- Четыре класса сбоев: crash (узел упал), omission (пакет потерян), timing (ответ опоздал), Byzantine (узел врёт)
- Уровни согласованности от сильной к слабой: linearizable - sequential - causal - eventual; каждый следующий быстрее, но менее предсказуем для клиента
Вопросы для размышления
- Любой ежедневный сервис (банковское приложение, мессенджер, навигатор) - какой trade-off CAP он скорее всего выбрал и почему именно такой?
Связанные уроки
- ds-02-cap-theorem — CAP формализует partition trade-off, заявленный здесь
- ds-03-consensus — Paxos и Raft решают согласие при описанных тут сбоях
- ds-05-replication — Стратегии репликации опираются на независимые сбои
- sd-10-microservices — Любой mesh микросервисов наследует модель сбоев Лэмпорта
- alg-01-big-o — Интуиция сложности нужна для рассуждений о масштабе кластера
- st-01-feedback-loops — Координация в организациях даёт ту же динамику частичных сбоев
- ibd-21-docker-k8s-interview
- net-01-intro