Пошук уроків, статей та іншого контенту
Розберемо обмеження CAP і PACELC та визначимо, як доступність, узгодженість і затримка впливають на архітектуру.
Розподілена система складається з кількох вузлів, які обмінюються даними мережею. Мережа може бути повільною, тимчасово недоступною або розділити вузли на ізольовані групи.
У такій системі неможливо одночасно гарантувати всі бажані властивості за будь-яких умов. CAP і PACELC допомагають явно описати ці компроміси та зрозуміти, які властивості важливіші для конкретного продукту.
CAP описує три властивості розподіленої системи:
Consistency — узгодженість: кожне читання отримує найновіше успішно записане значення або помилку.
Availability — доступність: кожен запит отримує відповідь, яка не є помилкою, навіть якщо частина вузлів недоступна.
Partition tolerance — стійкість до розділення мережі: система продовжує працювати, коли вузли не можуть обмінюватися повідомленнями.
Ключовий висновок CAP:
Під час мережевого розділення система змушена обрати між узгодженістю та доступністю.
Це не означає, що система загалом може мати лише дві з трьох властивостей. У реальних розподілених системах мережеві розділення потрібно вважати можливими, тому практичний вибір зазвичай відбувається між:
CP — Consistency + Partition tolerance;
AP — Availability + Partition tolerance.
Мережеве розділення виникає, коли вузли працюють, але не можуть зв’язатися один з одним.
Наприклад, є два вузли:
Клієнт A ── Node 1 ✕ Node 2 ── Клієнт BОбидва вузли можуть бути запущені, але повідомлення між ними не проходять. Якщо клієнт записує різні значення на різні вузли, система має вирішити:
приймати записи локально та залишатися доступною;
блокувати або відхиляти записи, щоб не допустити розбіжності.
Одночасно гарантувати обидві властивості неможливо.
CP-система під час розділення мережі зберігає узгодженість, але може відмовити частині запитів.
Наприклад, для запису система може вимагати підтвердження від кворуму вузлів. Якщо кворум недоступний, запис не виконується.
Це підходить для даних, де суперечливі значення небезпечні:
фінансові операції;
залишки на рахунках;
блокування ресурсів;
унікальні ідентифікатори;
стани, які не можна одночасно призначити двом користувачам.
Ціна CP-підходу:
частина запитів може отримати помилку;
під час проблем у мережі зростає час очікування;
для роботи потрібен доступний кворум.
AP-система під час розділення мережі продовжує обробляти запити, але різні вузли можуть тимчасово бачити різні дані.
Після відновлення зв’язку вузли синхронізуються. Такий підхід часто називають eventual consistency — зрештою узгодженість.
AP-підхід підходить для:
стрічок новин;
лічильників переглядів;
кешів;
рекомендацій;
каталогів, де короткочасна застарілість допустима;
функцій, для яких краще показати старі дані, ніж повідомити про помилку.
Ціна AP-підходу:
читання може повернути застаріле значення;
можуть виникати конфлікти записів;
потрібна стратегія їх розв’язання;
користувачі можуть тимчасово бачити різні результати.
Уявімо два репліки сховища, A і B, які зберігають баланс користувача.
До розділення:
A: 100
B: 100Після втрати зв’язку клієнт надсилає запис на A:
A: 50
B: 100Якщо A прийме запис, система залишається доступною, але репліки розійшлися. Це AP-поведінка.
Якщо A відмовить у записі, доки не зможе підтвердити його на B, значення залишаться узгодженими. Але операція стане недоступною. Це CP-поведінка.
Нижче — спрощена runnable-симуляція цього компромісу на Node.js:
class Replica {
constructor(name, value) {
this.name = name;
this.value = value;
this.connected = true;
}
write(value) {
this.value = value;
}
read() {
return this.value;
}
}
class ReplicatedStore {
constructor() {
this.replicas = [
new Replica("A", 100),
new Replica("B", 100),
];
}
setPartitioned(isPartitioned) {
for (const replica of this.replicas) {
replica.connected = !isPartitioned;
}
}
write(value, mode) {
const [primary, secondary] = this.replicas;
if (mode === "CP") {
if (!primary.connected || !secondary.connected) {
return {
ok: false,
error: "Кворум недоступний",
};
}
primary.write(value);
secondary.write(value);
return {
ok: true,
message: "Запис підтверджено обома репліками",
};
}
if (mode === "AP") {
primary.write(value);
if (primary.connected && secondary.connected) {
secondary.write(value);
}
return {
ok: true,
message: "Запис прийнято доступною реплікою",
};
}
throw new Error(`Невідомий режим: ${mode}`);
}
read(replicaName) {
const replica = this.replicas.find(
(item) => item.name === replicaName
);
if (!replica) {
throw new Error(`Репліку ${replicaName} не знайдено`);
}
return replica.read();
}
printState() {
console.log(
this.replicas
.map((replica) => `${replica.name}: ${replica.value}`)
.join(" | ")
);
}
}
const cpStore = new ReplicatedStore();
cpStore.setPartitioned(true);
console.log("CP під час розділення:");
console.log(cpStore.write(50, "CP"));
cpStore.printState();
const apStore = new ReplicatedStore();
apStore.setPartitioned(true);
console.log("\nAP під час розділення:");
console.log(apStore.write(50, "AP"));
apStore.printState();
console.log(
`\nЧитання з A: ${apStore.read("A")}, читання з B: ${apStore.read("B")}`
);Очікуваний результат має показати:
CP під час розділення:
{ ok: false, error: 'Кворум недоступний' }
A: 100 | B: 100
AP під час розділення:
{ ok: true, message: 'Запис прийнято доступною реплікою' }
A: 50 | B: 100
Читання з A: 50, читання з B: 100Це лише модель. Реальні сховища можуть використовувати кворуми, версії, журнали операцій, конфлікт-резолвери та інші механізми.
CAP стосується саме ситуації, коли система не може надійно комунікувати між вузлами.
Звичайна повільна мережа ще не обов’язково є розділенням. Однак на практиці система часто має встановлювати тайм-аути. Після тайм-ауту потрібно вирішити, чи:
чекати довше заради узгодженості;
швидко повернути старе значення;
повернути помилку;
прийняти локальний запис.
Тому межа між «повільно» та «недоступно» має важливі наслідки для архітектури.
PACELC розширює CAP і враховує поведінку системи не лише під час мережевого розділення.
Назва розшифровується так:
P — якщо відбулося розділення мережі;
A або C — система обирає доступність або узгодженість;
E — інакше, тобто коли розділення немає;
L або C — система обирає меншу затримку або узгодженість.
Формула:
Якщо є Partition, то обираємо Availability або Consistency; Else обираємо Latency або Consistency.
Тобто PACELC ставить два запитання:
Що робить система під час мережевого розділення?
Що робить система у звичайному режимі, коли мережа працює?
Навіть коли всі репліки доступні, сильніша узгодженість може вимагати додаткового обміну повідомленнями.
Наприклад, для читання система може:
відповісти з найближчої репліки;
дочекатися підтвердження від кількох реплік;
прочитати з лідера;
перевірити, що репліка має достатньо свіжі дані.
Локальне читання зазвичай має меншу затримку, але може повернути застаріле значення. Очікування підтверджень підвищує узгодженість, але збільшує latency.
Припустімо, репліки розташовані в різних регіонах:
Користувач ── Регіон A ── Регіон BЯкщо читати з репліки в регіоні A:
затримка буде малою;
дані можуть бути не найновішими.
Якщо кожне читання перевіряти в регіоні B:
можна отримати свіжіші дані;
зросте мережевий час;
тимчасова проблема між регіонами вплине на доступність.
Це і є компроміс L проти C у PACELC.
У контексті PACELC важливо розрізняти два підходи до читання.
Після успішного запису наступне читання бачить це значення незалежно від того, з якої репліки виконується читання.
Переваги:
простіша модель для клієнта;
менше шансів використати застарілі дані;
зручніше реалізовувати критичні операції.
Недоліки:
більша затримка;
більше залежностей від доступності реплік;
менша доступність під час проблем у мережі.
Після запису деякі репліки можуть тимчасово повертати старе значення. Якщо нові записи припиняться, репліки зрештою синхронізуються.
Переваги:
низька затримка;
краща доступність;
можливість працювати локально під час мережевих проблем.
Недоліки:
клієнт має бути готовим до застарілих даних;
конфлікти можуть вимагати додаткової логіки;
результат читання може залежати від обраної репліки.
Вибір не варто робити на рівні всієї системи без винятків. Різні операції можуть мати різні вимоги.
Для кожної важливої операції варто запитати:
Чи допустиме застаріле значення?
Чи може операція бути тимчасово відхилена?
Що станеться, якщо два вузли приймуть суперечливі записи?
Яка максимальна прийнятна затримка?
Чи повинен результат бути однаковим для всіх користувачів одразу?
Як система відновлюється після розділення?
Для платежу:
конфлікт між двома операціями неприйнятний;
краще відхилити запит, ніж створити неправильний баланс;
потрібна сильна узгодженість для критичної частини операції.
Для стрічки новин:
новий допис може з’явитися із затримкою;
краще показати кешовану стрічку, ніж повністю заблокувати перегляд;
допустима зрештою узгодженість.
Для наявності товару:
помилка під час покупки може бути кращою за продаж відсутнього товару;
перевірка залишку перед резервуванням має бути узгодженою;
відображення каталогу може бути менш узгодженим, ніж фактичне резервування.
Одна система може поєднувати різні підходи: наприклад, AP-читання каталогу та CP-операцію резервування.
CAP і PACELC впливають не лише на вибір бази даних. Вони визначають поведінку всієї системи:
чи використовуються тайм-аути;
чи повторюються невдалі запити;
чи дозволяються локальні записи;
чи повертається кешоване значення;
як клієнт дізнається про застарілі дані;
як синхронізуються репліки після відновлення;
як вимірюються затримка та частота конфліктів.
Важливо описати цю поведінку явно. Формулювання «система підтримує високу доступність» недостатнє без уточнення:
які запити залишаються доступними;
які дані можуть бути застарілими;
як довго може тривати розбіжність;
які помилки отримує клієнт;
що відбувається під час повторної синхронізації.
Це спрощення. У нормальному режимі система може бути і доступною, і узгодженою. Компроміс проявляється під час мережевого розділення, коли потрібно обрати між C і A.
У розподіленій системі мережеві проблеми неможливо повністю виключити. Відмова від P фактично означає відмову від роботи системи під час розділення.
У CAP доступність означає отримання успішної відповіді на запит. Вона не гарантує, що відповідь містить найсвіжіше значення.
AP-система може мати зрештою узгодженість. Розбіжність часто є тимчасовою, але система повинна мати механізм синхронізації та розв’язання конфліктів.
PACELC — не окрема класифікація за трьома літерами. Це спосіб описати два різні компроміси:
під час розділення мережі: доступність проти узгодженості;
у звичайному режимі: затримка проти узгодженості.
Навіть якщо сховище має певну модель узгодженості, різні операції можуть використовувати різні рівні гарантій. Архітектура застосунку, кеші, черги та спосіб читання також впливають на фактичну поведінку.
CAP описує компроміс між узгодженістю та доступністю під час мережевого розділення.
У розподілених системах стійкість до розділення зазвичай вважають необхідною.
CP-системи можуть відмовляти в операціях заради узгодженості.
AP-системи продовжують роботу, допускаючи тимчасову розбіжність даних.
PACELC додатково враховує звичайний режим: менша затримка або сильніша узгодженість.
Сильна узгодженість зазвичай потребує більшої кількості мережевих взаємодій.
Вибір потрібно робити для конкретних операцій і бізнес-вимог, а не лише для всієї системи загалом.