← Назад ко всем вопросам

Как сделать распределённую транзакцию

1️⃣ Как кратко ответить

Для реализации распределённой транзакции в Go можно использовать паттерн "Саги" или протокол двухфазного коммита (2PC). Паттерн "Саги" подходит для микросервисной архитектуры, обеспечивая компенсационные действия в случае неудачи. Протокол 2PC обеспечивает атомарность, но может быть сложным в реализации и менее устойчивым к сбоям.

2️⃣ Подробное объяснение темы

Распределённые транзакции необходимы, когда нужно обеспечить согласованность данных между несколькими системами или сервисами. Это часто встречается в микросервисной архитектуре, где каждый сервис может иметь свою базу данных.

Паттерн "Саги"

Паттерн "Саги" — это последовательность транзакций, где каждая транзакция имеет соответствующее компенсационное действие. Если одна из транзакций не удаётся, выполняются компенсационные действия для всех предыдущих успешных транзакций.

Пример использования паттерна "Саги":

Представим, что у нас есть система бронирования путешествий, состоящая из нескольких микросервисов: бронирование рейса, бронирование отеля и аренда автомобиля. Каждое из этих действий — это отдельная транзакция.

  1. Начало саги: Бронирование рейса.
  2. Компенсационное действие: Отмена бронирования рейса.
  3. Следующая транзакция: Бронирование отеля.
  4. Компенсационное действие: Отмена бронирования отеля.
  5. Следующая транзакция: Аренда автомобиля.
  6. Компенсационное действие: Отмена аренды автомобиля.

Если аренда автомобиля не удалась, выполняются компенсационные действия: отмена бронирования отеля и рейса.

Протокол двухфазного коммита (2PC)

Протокол 2PC обеспечивает атомарность транзакций в распределённых системах. Он состоит из двух фаз: подготовка и фиксация.

Пример использования 2PC:

  1. Фаза подготовки:

    • Координатор отправляет запрос на подготовку всем участникам транзакции.
    • Каждый участник выполняет локальную транзакцию и отвечает "готов" или "отказ".
  2. Фаза фиксации:

    • Если все участники ответили "готов", координатор отправляет команду на фиксацию.
    • Если хотя бы один участник ответил "отказ", координатор отправляет команду на откат.

Пример кода на Go:

package main
​
import (
	"fmt"
	"sync"
)
​
// Участник транзакции
type Participant struct {
	name string
}
​
// Метод подготовки участника
func (p *Participant) Prepare() bool {
	fmt.Printf("%s: подготовка\n", p.name)
	// Логика подготовки
	return true // Возвращает true, если подготовка успешна
}
​
// Метод фиксации участника
func (p *Participant) Commit() {
	fmt.Printf("%s: фиксация\n", p.name)
	// Логика фиксации
}
​
// Метод отката участника
func (p *Participant) Rollback() {
	fmt.Printf("%s: откат\n", p.name)
	// Логика отката
}
​
// Координатор транзакции
type Coordinator struct {
	participants []*Participant
}
​
// Метод выполнения двухфазного коммита
func (c *Coordinator) TwoPhaseCommit() {
	var wg sync.WaitGroup
	prepareChan := make(chan bool, len(c.participants))
​
	// Фаза подготовки
	for _, participant := range c.participants {
		wg.Add(1)
		go func(p *Participant) {
			defer wg.Done()
			prepareChan <- p.Prepare()
		}(participant)
	}
​
	wg.Wait()
	close(prepareChan)
​
	// Проверка результатов подготовки
	commit := true
	for result := range prepareChan {
		if !result {
			commit = false
			break
		}
	}
​
	// Фаза фиксации или отката
	if commit {
		for _, participant := range c.participants {
			participant.Commit()
		}
	} else {
		for _, participant := range c.participants {
			participant.Rollback()
		}
	}
}
​
func main() {
	// Создание участников
	p1 := &Participant{name: "Участник 1"}
	p2 := &Participant{name: "Участник 2"}
​
	// Создание координатора
	coordinator := &Coordinator{
		participants: []*Participant{p1, p2},
	}
​
	// Выполнение двухфазного коммита
	coordinator.TwoPhaseCommit()
}
  • Participant: представляет участника транзакции с методами Prepare, Commit и Rollback.
  • Coordinator: управляет процессом двухфазного коммита, координируя участников.
  • TwoPhaseCommit: метод, который выполняет две фазы: подготовку и фиксацию/откат.

Распределённые транзакции сложны в реализации и могут влиять на производительность системы. Паттерн "Саги" более гибок и устойчив к сбоям, тогда как 2PC обеспечивает строгую атомарность, но может быть менее устойчивым в случае отказов. Выбор подхода зависит от требований к согласованности и доступности системы.

Тема: Базы данных и SQL
Стадия: Tech

🔒 Подпишись на бусти автора и стань Алигатором, чтобы получить полный доступ к функционалу сайта и отслеживать свой прогресс!

Твои заметки