Пошук уроків, статей та іншого контенту
Порівняєте сильну, eventual та causal consistency і визначите компроміси між свіжістю даних та продуктивністю.
У розподіленій системі одна й та сама інформація часто зберігається на кількох вузлах — репліках. Через мережеву затримку або тимчасові збої репліки можуть оновлюватися не одночасно.
Узгодженість даних визначає, яке значення побачить клієнт під час читання та які гарантії система надає щодо порядку операцій.
Наприклад, користувач змінив статус замовлення з pending на paid. Якщо наступний запит прочитає старе значення pending, система має слабшу модель узгодженості, ніж система, яка одразу повертає paid.
Узгодженість потрібно відрізняти від:
Доступності — чи відповідає система на запити.
Надійності — чи збережуться дані після збою.
Транзакційності — чи виконується група операцій атомарно.
Свіжості даних — наскільки прочитане значення близьке до найновішого.
Сильна узгодженість зазвичай означає, що кожне читання повертає найновіше успішно записане значення.
Найсильнішою поширеною гарантією є linearizability:
кожна операція виглядає так, ніби виконалася в один конкретний момент;
операції зберігають порядок реального часу;
якщо запис завершився до початку читання, читання повинно побачити цей запис.
Клієнт записує balance = 100.
Сервер підтверджує успішний запис.
Клієнт читає баланс.
Читання повертає 100, а не попереднє значення.
Для реалізації сильної узгодженості система може:
направляти читання на лідер;
чекати підтвердження запису від достатньої кількості реплік;
блокувати читання під час вибору нового лідера;
використовувати синхронну реплікацію.
Просте розуміння поведінки системи.
Передбачувані результати для клієнта.
Зручно реалізовувати операції, де старе значення небезпечне.
Приклади:
списання коштів;
резервування останнього товару;
зміна прав доступу;
перевірка унікальності імені користувача.
Сильна узгодженість часто збільшує:
затримку запису;
затримку читання;
кількість мережевих взаємодій;
залежність від доступності лідера або кворуму.
Якщо репліка або частина мережі недоступна, система може відмовитися від операції замість того, щоб повернути потенційно застарілі дані.
Сильна узгодженість не означає, що дані автоматично зберігаються назавжди. Вона описує порядок і видимість операцій, а не довговічність запису.
Eventual consistency, або узгодженість у кінцевому підсумку, дозволяє реплікам тимчасово мати різні значення. Якщо нові записи припиняться, а мережа та вузли відновлять роботу, усі репліки зрештою прийдуть до одного стану.
Типовий сценарій:
Запис потрапляє на одну репліку.
Система одразу відповідає клієнту.
Інші репліки отримують зміни пізніше.
Протягом короткого часу різні клієнти можуть бачити різні значення.
Після реплікації значення стають однаковими.
Низька затримка записів.
Вища доступність під час проблем із мережею.
Можливість обслуговувати читання з найближчої репліки.
Краще масштабування для великої кількості читань.
Клієнт може побачити застаріле значення.
Значення може тимчасово «відкотитися» під час читання з різних реплік.
Конфлікти потрібно виявляти та розв’язувати.
Бізнес-логіка має враховувати затримку реплікації.
Eventual consistency добре підходить для:
лічильників переглядів;
стрічок новин;
кешів;
пошукових індексів;
аналітики;
лайків і реакцій;
каталогів, де затримка оновлення на кілька секунд прийнятна.
Eventual consistency не гарантує конкретний час сходження реплік. «Зрештою» означає, що за умови відсутності нових конфліктних змін і нормальної роботи системи репліки синхронізуються, але точний строк залежить від архітектури та навантаження.
Causal consistency, або причинно-наслідкова узгодженість, зберігає порядок операцій, між якими існує причинний зв’язок.
Якщо одна операція залежить від іншої, клієнти не повинні побачити їх у зворотному порядку.
Користувач створює допис.
Після успішного створення інший користувач додає коментар до цього допису.
Причина появи коментаря — існування допису. Система з causal consistency не повинна показати коментар у відповідь, де самого допису ще немає.
Водночас дві незалежні операції можуть бути побачені в різному порядку:
Користувач A створив допис.
Користувач B оновив профіль.
Між цими операціями немає причинного зв’язку, тому система не зобов’язана встановлювати для них єдиний глобальний порядок.
Причинний зв’язок може виникати, коли:
одна операція виконується після читання результату іншої;
одна операція є продовженням попередньої операції в тій самій сесії;
клієнт явно передає версію або токен попереднього запису;
одна подія породжує іншу.
Для відстеження причинності система може використовувати:
версії записів;
логічні годинники;
vector clocks;
токени сесії;
ідентифікатори позиції в журналі подій.
У простому випадку клієнт передає серверу версію останнього запису, який він уже бачив. Репліка може відповісти лише після того, як застосує цю версію або пізнішу.
Менша затримка порівняно з повністю сильною узгодженістю.
Збереження логічного порядку пов’язаних подій.
Зручна модель для соціальних мереж, чатів і систем на основі подій.
Складніша реалізація.
Потрібно передавати та зберігати інформацію про залежності.
Незалежні записи все ще можуть приходити в різному порядку.
Конфлікти між паралельними змінами не зникають автоматично.
Розглянемо три способи читання після запису:
Клієнт записав status = "paid"
Клієнт одразу читає statusЧитання гарантовано повертає:
"paid"Система може довше чекати або тимчасово відмовити в обслуговуванні.
Читання може повернути:
"pending"Пізніше, після реплікації, воно поверне:
"paid"Якщо читання передає залежність від запису, воно не повинно повернути значення старше за paid. Однак інший клієнт без цієї залежності може тимчасово прочитати pending.
У прикладі є головна репліка та віддалена репліка. Запис застосовується одразу на головній репліці, а до віддаленої доходить із затримкою.
const primary = {
value: "pending",
version: 0
};
const replica = {
value: "pending",
version: 0
};
function writeStatus(value) {
primary.value = value;
primary.version += 1;
const version = primary.version;
// Імітуємо затримку реплікації
setTimeout(() => {
replica.value = value;
replica.version = version;
console.log(
`Репліка оновлена: ${replica.value}, версія ${replica.version}`
);
}, 1000);
return {
value,
version
};
}
function readStrong() {
// Сильне читання виконується з головної репліки
return {
source: "primary",
value: primary.value,
version: primary.version
};
}
function readEventual() {
// Читання з репліки може повернути застаріле значення
return {
source: "replica",
value: replica.value,
version: replica.version
};
}
function readCausally(minVersion) {
return new Promise((resolve) => {
const timer = setInterval(() => {
if (replica.version >= minVersion) {
clearInterval(timer);
resolve({
source: "replica",
value: replica.value,
version: replica.version
});
}
}, 100);
});
}
async function main() {
const writeResult = writeStatus("paid");
console.log("Сильне читання:", readStrong());
console.log("Eventual-читання:", readEventual());
// Причинне читання чекає, поки репліка застосує потрібну версію
console.log(
"Causal-читання:",
await readCausally(writeResult.version)
);
}
main();Після запису результат приблизно такий:
Сильне читання: { source: 'primary', value: 'paid', version: 1 }
Eventual-читання: { source: 'replica', value: 'pending', version: 0 }
Репліка оновлена: paid, версія 1
Causal-читання: { source: 'replica', value: 'paid', version: 1 }Це спрощена модель. У реальній системі причинні залежності можуть бути складнішими, ніж одна послідовна версія, а для паралельних записів можуть знадобитися vector clocks або інші механізми.
Свіжіші дані зазвичай потребують додаткової координації між вузлами.
Оберіть сильні гарантії, якщо помилка через застарілі дані може мати критичні наслідки:
баланс рахунку;
залишок дефіцитного товару;
права доступу;
стан платежу;
блокування ресурсу;
унікальні бізнес-обмеження.
Тут краще прийняти більшу затримку або тимчасову недоступність, ніж виконати операцію на неправильних даних.
Eventual consistency доречна, якщо:
затримка оновлення прийнятна;
дані переважно читаються;
користувач може повторити запит;
невелика тимчасова неточність не шкодить бізнесу;
система має працювати навіть при частковій недоступності.
Наприклад, лічильник переглядів може оновлюватися із затримкою. Немає потреби блокувати весь сервіс через те, що одна репліка ще не отримала нове значення.
Causal consistency корисна, коли важливий порядок пов’язаних дій, але глобально синхронізувати всі операції занадто дорого:
повідомлення у чаті;
публікація та відповіді на неї;
події в межах однієї користувацької сесії;
ланцюжки команд у системі на основі подій;
оновлення документа кількома користувачами.
Одна система може використовувати різні гарантії для різних даних.
Наприклад:
баланс рахунку читати сильно узгоджено;
список рекомендацій читати з eventual consistency;
повідомлення в чаті читати з causal consistency;
пошуковий індекс оновлювати асинхронно.
Це дає змогу не застосовувати найдорожчу модель до всієї системи. Для кожної операції варто окремо визначити:
Яку затримку може прийняти користувач?
Чи допустиме застаріле значення?
Чи може операція залежати від попереднього читання?
Які наслідки матиме конфлікт?
Чи важливіша доступність за негайну свіжість?
Eventual consistency не означає хаотичні або випадкові дані. Вона має гарантію: за певних умов репліки зрештою збіжаться до узгодженого стану.
Сильна модель збільшує вартість координації. Для кешу або стрічки новин вона може бути непотрібною і погіршити продуктивність.
Вона зберігає порядок причинно пов’язаних операцій, але незалежні паралельні операції можуть мати різний порядок на різних вузлах.
Навіть якщо запис уже завершився, наступне читання може потрапити на репліку, яка ще не отримала це оновлення.
Eventual і causal consistency не вирішують усі конфлікти автоматично. Якщо дві репліки незалежно змінили один запис, потрібна стратегія розв’язання:
останній запис перемагає;
злиття значень;
пріоритет джерела;
ручне вирішення;
спеціальна структура даних.
Якщо клієнт прочитав одну зміну, а потім створив залежну від неї зміну, система має знати про цей зв’язок. Без версії, токена або іншого контексту причинну узгодженість неможливо надійно гарантувати.
Сильна узгодженість гарантує читання найновішого значення, але може збільшити затримку та зменшити доступність.
Eventual consistency дозволяє тимчасово читати застарілі дані заради продуктивності та доступності.
Causal consistency зберігає порядок причинно пов’язаних операцій, але не встановлює глобальний порядок для незалежних змін.
Вибір моделі залежить від наслідків застарілого читання, допустимої затримки та вимог до доступності.
В одній системі можна поєднувати різні моделі узгодженості для різних типів даних і операцій.
Сильні гарантії потрібні там, де неправильне або старе значення може призвести до фінансової, безпекової чи бізнес-помилки.