Проблема розподіленої атомарності
Однономний базис даних транзакції легкий для забезпечення атомарності: двигун зберігання або записує всі зміни на диск, або ні, і аварія посередині запису просто скасовується при перезавантаженні. Розподілені транзакції складніші, оскільки робота розподілена між кількома незалежними машинами, кожна з яких має власний локальний диск, власний годинник та власний шанс аварійно вийти з ладу або втратити мережеве підключення в будь-який момент. Якщо транзакція оновлює дані на трьох окремих вузлах бази даних, необхідно, щоб усі три оновлення пройшли разом або всі три повинні зазнати невдачі, забезпечуючи атомарність між машинами, незважаючи на те, що жодна машина в реальному часі не може бачити, що роблять інші. Без координації часткова аварія (вузол А підтверджує, вузол B виходить з ладу перед завершенням) залишає систему в невідповідному стані: гроші дебетуються з одного рахунку, але ніколи не кредитуються на інший, замовлення позначено як відправлене в одній службі, але ніколи не записується як оплачений у сервісі обробки платежів. Це основна проблема, яку розроблено протоколу двофазного комітування, щоб вирішити: перетворення набору незалежних, ненадійних локальних рішень на одну надійну, спільну, все-або-нічого рішення. Протокол вводить дві ролі для забезпечення цього: координатор, який керує процесом, та один або більше учасників, які зберігають фактичні дані та виконують справжню роботу транзакції.
Фаза 1: Підготовка та Голосування
Перша фаза 2PC – це раунд голосування. Координатор надсилає запит на підготовку кожному учаснику, по суті питаючи, чи може ви гарантувати успіх цієї транзакції, якщо я пізніше наказаю вам її підтвердити? Кожен учасник виконує фактично роботу транзакції локально, застосовуючи зміни таким чином, що їх можна буде скасувати, і критично важливо, записує результат у надійний журнал на диску перед відповіддю. Цей крок стійкості має величезне значення: коли учасник записує підготовку до «так» у свій журнал, він робить зобов’язання, що може підтвердити пізніше навіть якщо він збоїть і перезавантажиться, оскільки відтворить журнал і запам'ятає свою обіцянку. Кожен учасник потім відповідає голосом: «так/готовий», що означає, що він виконав роботу, заблокував необхідні ресурси та повністю готовий до підтвердження, або «не/відмова», що означає, що сталася помилка, така як порушення обмежень, конфлікт блокування або локальна несправність, і транзакція не може продовжитися. Координатор збирає всі голоси перед тим, як рухатися далі. Ця фаза перетворює невизначену майбутню дію на певне, зафіксоване в журналі зобов’язання, що робить другу фазу достовірною.
Фаза 2: Остаточне підтвердження або відмову
Після того, як координатор отримав відповіді від усіх учасників, він приймає остаточне рішення, використовуючи просте правило консенсусу. Якщо всі учасники проголосували «за», координатор записує власну рішу в надійний журнал і потім надсилає повідомлення про підтвердження кожному учаснику, який завершує транзакцію, звільняє замки та робить зміни постійно видимими. Якщо хоча б один учасник проголосував «проти», координатор замість цього надсилає повідомлення про відмову всім учасникам і кожен з них скасовує тимчасові зміни, які застосовувалися на першому етапі, ніби транзакції не було. Це правило консенсусу забезпечує атомічність: немає шляху через протокол, де деякі учасники підтверджують, а інші відмовляються, оскільки рішення координатора транслюється всім і кожен учасник зобов’язаний його дотримуватися. Учасники підтверджують остаточне повідомлення, лише після чого координатор вважає транзакцію повністю завершеною. Ззовні весь розподілений транзакційний процес поводиться як одна атомічна операція, незважаючи на те, що він торкався кількох незалежних машин, які ніколи не координувалися безпосередньо між собою.
Проблему блокування: Критична слабкість
Двофазна коммітність надійно гарантує атомарність, але має добре відому структурну ваду – проблему блокування. Розглянемо цей сценарій: троє учасників голосують ‘за’ під час фази 1, записуючи цю обіцянку в свої журнали та блокуючи свої ресурси. Координатор отримує всі три «так», і ось-ось відправить повідомлення про коміт, але перш ніж це зробити, він може вийти з ладу, наприклад, через несправність обладнання або мережеву сегментацію. Тепер кожен учасник застряг у невизначеному стані. Кожен із них обіцяв, що може підтвердити, тому не може односторонньо відмінити, оскільки координатор вже міг вирішити підтвердити та повідомити іншому учаснику. Але він також не може односторонньо підтвердити, оскільки координатор міг бути на межі надсилання повідомлення про відмову. Єдина безпечна опція – продовжувати утримувати свої блокування, блокуючи будь-які інші транзакції, які потребують тих самих ресурсів, і чекати відновлення координатора, щоб повідомити йому, що робити. Якщо координатор залишається без дію протягом тривалого періоду часу, ці блокування залишаються утримуватися впродовж усього цього часу, потенційно заморожуючи великі частини системи. Ця єдина точка відмови, де доля багатьох учасників повністю залежить від відновлення одного координатора, є основною критикою, висловленою щодо 2PC.
Мотивування трьохфазного коміту та протоколів консенсусу
Проблему блокування безпосередньо спонукала пошук більш стійких до збоїв альтернатив. Трьохфазний коміт (3PC) був раннім спробою її вирішити шляхом введення додаткової фази перед-коміту, надаючи учасникам достатньо спільного знання, щоб вони могли безпечно прогресувати самостійно, навіть якщо координатор зникне, хоча 3PC все ще бореться з мережевими поділами та ніколи не набув широкого розповсюдження, як це було з двофазним комітом. Більш впливовим шляхом стало використання алгоритмів консенсусу, таких як Paxos і Raft. Замість покладатися на одного координатора, відмова якого може заблокувати всю систему, ці протоколи реплікують рішення в межах кластеру вузлів і вимагають лише більшості кворуму для узгодження, а не одностайного участі кожного вузла. Якщо один вузол, навіть лідер, збоїть, залишок більшості може обрати нового лідера та продовжувати прогрес, оскільки жоден окремий збій машини не може заблокувати групу назавжди. Цей перехід від одного крихкого координатора, який приймає зобов'язання, до стійкої більшості, яка досягає узгодження разом, лежить в основі того, як сучасні розподілені бази даних, координаційні сервіси, такі як ZooKeeper і etcd, та системи розподіленого консенсусу досягають як атомічності, так і високої доступності, безпосередньо вирішуючи слабкість, яку класичному двофазному коміту ніколи не вдалося повністю подолати.
Frequently asked questions
Яка головна мета двофамітного коміту?
Головна мета – забезпечення атомарності в розподілених транзакціях на кількох незалежних вузлах: гарантування того, що кожен учасник або підтверджує транзакцію одночасно, або всі учасники скасовують її одночасно, без можливості, щоб деякі вузли підтверджували, а інші – ні.
Яка різниця між координатором і учасником?
Координатор – це єдиний вузол, який керує протоколом, надсилаючи запити на підготовку, збираючи голоси та розповсюджуючи остаточне рішення про підтвердження або скасування. Учасники – це вузли, які фактично зберігають дані, виконують роботу транзакції локально, голосують щодо можливості підтвердження та слідують остаточному наказу координатора.
Чому учасникам необхідно записувати свій голос у довготривале лог?
Запис голосу в довготривалий лог гарантує, що якщо учасник аварійно зупиняється та перезавантажується після голосування «так», він може відновити свою обіцянку з логу та все ще виконувати її пізніше, а не забути, що він пообіцяв бути готовим до підтвердження. Ця довготривалість робить голос «так» надійним та достовірним зобов’язанням.
Що таке саме блокуюча проблема в двофамітному коміті?
Блокуюча проблема виникає, коли координатор аварійно зупиняється після того, як учасники проголосували «так», але до того, як він відправив остаточне рішення про підтвердження або скасування. Учасники не можуть безпечно продовжувати роботу в жодному напрямку, тому вони повинні утримувати свої блокування та чекати, потенційно протягом тривалого часу, поки координатор не відновиться.
Як Paxos і Raft уникають блокуючої проблеми, яка є в двофамітному коміті?
Paxos та Raft реплікують рішення по всій кластеру та вимагають лише більшого кворуму для згоди, а не одностайної участі, координованої одним крихким вузлом. Якщо вузол лідера відмовляється, залишся більшість може обрати нового лідера та продовжувати прогрес, тому жодний збій одного вузла не може заморозити всю систему так, як може збій координатора в класичному двофамітному коміті.
Спробуйте наживо
Усе, що вище, працює прямо у вашому браузері — відкрийте Two-Phase Commit: How Distributed Databases Agree to Commit or Abort Together і змінюйте параметри під час роботи. Нічого не встановлюється, нічого не завантажується на сервер, уся модель живе в одній вкладці.
▶ Відкрити симуляцію Two-Phase Commit: How Distributed Databases Agree to Commit or Abort Together