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

Как обеспечивается сохранность информации при отключении одной из Node базы данных

3.0 Senior🔥 71 комментариев
#Docker, Kubernetes и DevOps#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Как обеспечивается сохранность информации при отключении Node БД

Концепция: Высокая доступность (High Availability)

Когда в распределённой БД отключается один из узлов (Node), данные должны остаться целыми благодаря репликации, избыточности и консистентности.

1. Репликация данных (Replication)

Master-Slave (Primary-Secondary) репликация

      Приложение
           ↓
     ┌─────────────┐
     │   Primary   │
     └─────────────┘
           ↓
    (Синхронная репликация)
           ↓
    ┌─────────────┐ ┌─────────────┐
    │ Secondary 1 │ │ Secondary 2 │
    └─────────────┘ └─────────────┘

2. Write-Ahead Logging (WAL)

Данные записываются дважды: сначала в лог, потом в БД. Если произойдёт сбой, БД восстановится из лога.

@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create()
            .driverClassName("org.postgresql.Driver")
            .url("jdbc:postgresql://localhost:5432/mydb")
            .username("postgres")
            .password("password")
            .build();
    }
}

3. Синхронная репликация (Synchronous Replication)

Процесс:

  1. Приложение отправляет WRITE на Primary
  2. Primary записывает в WAL
  3. Primary отправляет WAL на Replica1 и Replica2
  4. Replicas подтверждают получение
  5. Primary коммитит транзакцию
  6. Primary возвращает ACK приложению
@Service
public class OrderService {
    private final JdbcTemplate jdbcTemplate;
    
    public OrderService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    public void createOrder(Order order) {
        jdbcTemplate.update(
            "INSERT INTO orders (id, user_id, amount, created_at) VALUES (?, ?, ?, ?)",
            order.getId(),
            order.getUserId(),
            order.getAmount(),
            new Timestamp(System.currentTimeMillis())
        );
    }
}

4. Кворум (Quorum) - Majority-based replication

@Service
public class UserService {
    private final DataSource dataSource;
    
    public UserService(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    public void saveUser(User user) {
        // QUORUM = N/2 + 1 узлов должны подтвердить
        try (Connection conn = dataSource.getConnection()) {
            PreparedStatement stmt = conn.prepareStatement(
                "INSERT INTO users (id, name, email) VALUES (?, ?, ?)"
            );
            stmt.setObject(1, user.getId());
            stmt.setString(2, user.getName());
            stmt.setString(3, user.getEmail());
            stmt.execute();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

5. Failover автоматический

При отключении Primary:

  1. Monitor обнаруживает отключение
  2. Monitor выбирает нового Primary из Replicas
  3. Replica становится новым Primary
  4. Приложение автоматически переключается
@Configuration
public class HighAvailabilityConfig {
    
    @Bean
    public HikariConfig hikariConfig() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://primary.db.local:5432,secondary1.db.local:5432/mydb");
        config.setUsername("user");
        config.setPassword("password");
        config.setMaximumPoolSize(20);
        config.setConnectionTimeout(3000);
        return config;
    }
    
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource(hikariConfig());
    }
}

6. Backup и Recovery (PITR)

Point-In-Time Recovery позволяет восстановить БД на любой момент времени.

@Service
public class BackupService {
    
    public void performBackup() {
        // Полный backup через pg_basebackup
        Runtime runtime = Runtime.getRuntime();
        try {
            Process process = runtime.exec(
                "pg_basebackup -h localhost -U postgres -D /backups/backup1"
            );
            process.waitFor();
        } catch (Exception e) {
            throw new RuntimeException("Backup failed", e);
        }
    }
}

7. Monitoring и Health Checks

@RestController
public class HealthCheckController {
    
    private final DataSource dataSource;
    
    public HealthCheckController(DataSource dataSource) {
        this.dataSource = dataSource;
    }
    
    @GetMapping("/health/db")
    public ResponseEntity<DatabaseHealth> checkDatabaseHealth() {
        try (Connection conn = dataSource.getConnection()) {
            PreparedStatement stmt = conn.prepareStatement("SELECT 1");
            stmt.execute();
            
            return ResponseEntity.ok(new DatabaseHealth(
                true,
                "Database is healthy",
                "2 replicas active"
            ));
        } catch (SQLException e) {
            return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body(new DatabaseHealth(false, "Database unavailable", null));
        }
    }
}

class DatabaseHealth {
    public boolean healthy;
    public String message;
    public String replication;
    
    public DatabaseHealth(boolean healthy, String message, String replication) {
        this.healthy = healthy;
        this.message = message;
        this.replication = replication;
    }
}

8. Distributed Transactions и Consistency

@Service
public class OrderSaga {
    private final OrderService orderService;
    private final PaymentService paymentService;
    
    @Transactional
    public Order createOrderWithCompensation(CreateOrderRequest request) {
        try {
            Order order = orderService.create(request);
            PaymentResult payment = paymentService.charge(order);
            if (!payment.isSuccess()) {
                throw new PaymentFailedException();
            }
            return order;
        } catch (Exception e) {
            orderService.cancel(order);
            paymentService.refund(payment);
            throw e;
        }
    }
}

9. Connection Pooling with Failover

Основные параметры:

  • autoReconnect - автоматическое переподключение
  • maxReconnectAttempts - количество попыток
  • initialPoolSize - начальный размер пула
  • maxPoolSize - максимальный размер пула

10. Стратегии защиты данных

МеханизмУровеньГарантия
WALPrimaryВосстановление после сбоя
Синхронная репликацияReplicasДанные на 2+ узлах
КворумClusterБольшинство узлов должны подтвердить
FailoverClusterАвтоматическое переключение
PITRBackupВосстановление на любой момент

Best Practices

  1. Репликация на несколько узлов - минимум 3
  2. Синхронная репликация для критичных данных
  3. WAL архивирование для восстановления
  4. Автоматический failover механизм
  5. Health checks регулярная проверка
  6. Регулярные backups с проверкой восстановления
  7. Мониторинг репликации - лаг, статус
  8. Connection pooling с failover поддержкой
  9. Тестирование failover сценариев
  10. SLA и RTO/RPO - определи требования