open atlas
↑ К треку
Основы System Design SD · 04 · 04

CAP и PACELC

CAP узка: лишь во время сетевой партиции надо выбирать консистентность или доступность. PACELC её достраивает — иначе, без партиции, ты всё равно меняешь задержку на консистентность. Выбирай по кейсу вдоль спектра linearizable → eventual.

SD Middle ◷ 20 min
Уровень
ОсновыJuniorMiddleSenior

Senior-инженер говорит на ревью дизайна: «Мы на Cassandra, значит выбрали AP над CP — отдали консистентность ради доступности». Звучит авторитетно и неверно так, что это важно. CAP форсирует этот выбор лишь во время сетевой партиции — редкого события. Остальные 99,9% времени, когда сеть здорова, реальный, постоянный компромисс команды — это задержка против консистентности: каждое чтение либо платит за координацию реплик, либо возвращает возможно устаревшие данные быстро. CAP описывает момент кризиса; решение, формирующее систему каждый день, — то, о котором CAP даже не упоминает. PACELC упоминает. Разобраться в этих двух — это разница между карго-культом выбора базы и реальным пониманием того, что обещает твоё хранилище.

CAP, сформулированная точно

Теорема CAP (Брюер; доказана Гилбертом и Линчем) — о трёх свойствах распределённого хранилища. Прежде чем правильно использовать эти метки на ревью дизайна, нужно знать, что именно означает каждая — и что она не означает:

  • Консистентность (C) — каждое чтение видит самую свежую запись (конкретно — линеаризуемость: система ведёт себя так, будто копия данных одна и операции идут в едином порядке). Заметь: это не C из ACID — буква та же, идея другая.
  • Доступность (A) — каждый запрос к неупавшему узлу получает не-ошибочный ответ (пусть и не свежайшие данные).
  • Терпимость к партиции (P) — система продолжает работать, когда сеть роняет или задерживает сообщения между узлами.

Реальное содержание теоремы узко и условно: когда случается сетевая партиция, надо выбрать между C и A — оба нельзя. Если два узла не могут общаться и запись легла на один, другой либо отказывается отвечать (жертвуя A ради консистентности → CP), либо отвечает своими возможно устаревшими данными (жертвуя C ради доступности → AP). Вот и всё. Доказательство почти тривиально, едва сформулировано так: партиционированный узел, всё же отвечающий, либо лжёт (не C), либо встаёт (не A).

Почему CAP так часто перевирают

Три мифа, все стоит разучить:

  1. «Выбери два из трёх». Уронить P нельзя. В любой реальной распределённой системе сетевые партиции будут — это факт физики и эксплуатации, а не выбор дизайна (Amazon Builders’ Library прямо говорит, что независимые отказы и партиции неизбежны). Так что P обязателен, и живой выбор — лишь C-против-A пока идёт партиция. «Выбери два» звучит так, будто CA — опция; для распределённого хранилища это не так.
  2. «Моя база AP/CP, точка». Выбор может быть на операцию, а не на систему. DynamoDB по умолчанию делает eventual-консистентные чтения (AP-подобно), но предлагает флаг ConsistentRead для строго консистентных чтений той же таблицы. Многие системы дают выбирать консистентность на запрос, так что навешивание одной буквы на всю базу прячет реальное настраиваемое решение.
  3. CAP описывает всю систему. Она описывает поведение лишь во время партиции. Инженер из hook схлопнул «что мы делаем в кризис» в «что есть наша система» и упустил компромисс, реально присутствующий каждый день, — ровно то, что добавляет PACELC.

Все три мифа вытекают из одного корня: CAP воспринимают как широкую классификацию системы, а не как условное утверждение — только о моменте партиции. Когда увидишь метку «AP» или «CP» в дизайн-доке, твой следующий вопрос: «на операцию или на систему?» и «а что происходит, когда сеть здорова?»

PACELC: остальное время

PACELC (Абади) расширяет CAP на нормальную работу:

PAC | ELC
Если Partition  → выбери Availability или Consistency
Else (норма)    → выбери Latency      или Consistency

Читай как: если есть Partition, меняй A против C (это CAP); Else (иначе) меняй L против C. Ветвь «иначе» — это инсайт. При здоровой сети строго консистентное чтение всё равно должно координироваться через реплики (ждать кворум, говорить с лидером) — что стоит задержки. Чтобы быстрее, читаешь с близкой реплики, которая может слегка устареть, — что стоит консистентности. Так что даже при нуле партиций ты постоянно выбираешь, сколько задержки платить за сколько свежести. Этот компромисс присутствует на каждом запросе, поэтому формирует систему куда сильнее, чем редкая партиция.

Базы получают двусоставную метку: DynamoDB и Cassandra — PA/EL (доступны при партиции, низкая задержка, но устаревшее иначе); строго консистентное хранилище вроде однолидерной SQL-базы или Spanner склоняется к PC/EC (консистентно при партиции, консистентно, но выше задержка иначе). Метка наконец схватывает и кризисное, и повседневное поведение.

Почему это работает

Почему строгая консистентность стоит задержки даже когда ничего не сломано? Потому что «каждое чтение видит свежайшую запись» требует, чтобы чтение было уверено, что нигде нет более свежей записи, — а единственный способ убедиться — координация: маршрутизировать через единственного лидера (урок 01) или читать с кворума реплик (R + W > N), оба добавляют round trips, а через регион эти round trips — те самые ~150 мс «скорости света» из юнита масштабирования. Eventual-консистентное чтение пропускает координацию и отвечает с ближайшей копии немедленно — быстро, но может упустить ещё распространяющуюся запись. Так что задержка-против-консистентности — не изъян реализации; это та же физика, что и лаг репликации, со стороны чтения. Ветвь «иначе» PACELC лишь именует цену координации, всегда там присутствующую, партиция или нет.

Спектр консистентности и выбор по кейсу

C-против-A и L-против-C не бинарны; это спектр моделей консистентности (Jepsen каталогизирует их как свойства безопасности — какие истории система вправе порождать). От сильнейших к слабейшим, несущие ступени:

  • Линеаризуемая (строгая) — чтения всегда видят свежайшую запись; система ведёт себя как одна копия. Сильнейшая, дороже всех, наименее доступна при партиции.
  • Последовательная / каузальная — операции уважают консистентный порядок (каузальная: следствия не появляются раньше причин — ты никогда не видишь ответ раньше сообщения, на которое он отвечает).
  • Read-your-writes / монотонные чтения — гарантии на клиента (видишь свои записи; чтения не идут назад) без глобальной силы — починка-маршрутизация урока 01 живёт здесь.
  • Eventual — реплики сходятся, если записи прекратятся, без обещаний порядка или свежести тем временем. Слабейшая, дешевейшая, доступнейшая.

Senior-ход — выбирать по кейсу, а не по компании:

  • Баланс банка или декремент склада, который не должен перепродать → линеаризуемая / CP / EC: корректность важнее доступности и задержки.
  • Счётчик лайков, лента, метка «последний раз в сети», счётчик просмотров страницы товара → eventual / AP / EL: пара секунд устаревания невидима пользователю и покупает огромную доступность и скорость.
  • Пользователь, редактирующий собственный профиль → read-your-writes достаточно: он должен видеть своё изменение, но глобальная строгая консистентность избыточна.

Поэтому одна компания держит Postgres для заказов и Cassandra/DynamoDB для ленты активности: точку на спектре выбирают данные, а не догма.

Выбери лучший вариант

Интернет-магазин управляет тремя типами данных: (A) счётчики остатков для баннеров 'последние 3 на складе', (B) авторитетное уменьшение остатка, предотвращающее оверселл при покупке, (C) счётчик просмотров товара в листинге. Какая модель консистентности подходит для (B)?

Частая ошибка

Дорогая ошибка — применять один уровень консистентности ко всей системе. Требовать линеаризуемость везде «на всякий случай» — значит купить цену задержки и доступности CP/EC на данных, которым она не нужна (твоему счётчику просмотров — нет), делая весь продукт медленнее и хрупче при партиции без выгоды. Обратная ошибка не лучше: гонять деньги или склад на eventual-консистентном хранилище, потому что оно «web scale», а затем обнаружить, что перепродал билеты или дал балансу уйти в минус при партиции. Согласуй модель консистентности с реальным требованием корректности каждого куска данных — и помни, что она часто настраиваема на запрос (ConsistentRead DynamoDB, уровни кворума), так что можно держать большинство чтений дёшевыми и заставить платить лишь те немногие, что обязаны быть строгими. Вся машинерия, что реализует строгую консистентность при партиции — кворумы, выбор лидера, Raft, — это работа трека distributed; здесь навык — выбрать правильную точку, а не построить её.

Викторина

Коллега говорит «CAP значит выбери два из C, A, P». Почему это вводящая в заблуждение формулировка для реальной распределённой системы?

Викторина

Твоя сеть идеально здорова — ни одной партиции за квартал. По PACELC, какой компромисс твоя строго консистентная база всё равно делает на каждом чтении и почему?

Закончи аналогию

PACELC читается так: если есть Partition, меняй доступность на консистентность; _______, меняй задержку на консистентность — так что здоровая сеть всё равно форсирует постоянный выбор между быстрыми-но-устаревшими и медленными-но-свежими чтениями.

Вспомните перед уходом
  1. 01
    Сформулируй CAP точно и объясни три частых способа её переврать.
  2. 02
    Что PACELC добавляет к CAP и почему ветвь «иначе» доминирует на практике?
  3. 03
    Набросай спектр консистентности и как выбирают точку по кейсу.
Итог

CAP узка и условна: лишь во время сетевой партиции распределённое хранилище должно выбрать между Консистентностью (линеаризуемость — каждое чтение видит свежайшую запись, не C из ACID) и Доступностью (каждый неупавший узел отвечает), давая CP (отказ в устаревшем) или AP (отдать устаревшее). Её широко перевирают — «выбери два из трёх» неверно, ведь партиции неизбежны, так что P обязателен; метка часто на операцию (ConsistentRead DynamoDB); и CAP ничего не говорит о нормальной работе. PACELC её достраивает: если Partition → A против C; Else → задержка против консистентности. Ветвь «иначе» доминирует на практике, ведь каждое чтение при здоровой сети всё равно выбирает между координацией ради свежести (задержка) и чтением близкой устаревшей копии (скорость) — та же физика, что и лаг репликации, со стороны чтения. Консистентность — спектр: линеаризуемая → последовательная/каузальная → read-your-writes/монотонные → eventual (свойства безопасности Jepsen; см. jepsen.io) — и senior-навык в том, чтобы выбирать по кейсу: строго для денег и склада, eventual для счётчиков лайков и лент, read-your-writes для собственных правок пользователя, часто настраиваемо на запрос. Применять один уровень везде — дорогая ошибка в обе стороны. Машинерия, что реализует строгую консистентность при партиции — кворумы, выбор лидера, Raft, — это работа трека distributed; работа этого юнита — выбрать правильную точку на спектре, раз уж данные распределены. Теперь, когда на ревью услышишь «мы выбрали AP» или «мы выбрали CP», ты спросишь: при партиции или всегда? — и какой уровень консистентности каждый запрос действительно требует.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 7 завершено
Связанные уроки

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.