← Назад к вопросам
Как обеспечивается сохранность информации при отключении одной из 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)
Процесс:
- Приложение отправляет WRITE на Primary
- Primary записывает в WAL
- Primary отправляет WAL на Replica1 и Replica2
- Replicas подтверждают получение
- Primary коммитит транзакцию
- 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:
- Monitor обнаруживает отключение
- Monitor выбирает нового Primary из Replicas
- Replica становится новым Primary
- Приложение автоматически переключается
@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. Стратегии защиты данных
| Механизм | Уровень | Гарантия |
|---|---|---|
| WAL | Primary | Восстановление после сбоя |
| Синхронная репликация | Replicas | Данные на 2+ узлах |
| Кворум | Cluster | Большинство узлов должны подтвердить |
| Failover | Cluster | Автоматическое переключение |
| PITR | Backup | Восстановление на любой момент |
Best Practices
- Репликация на несколько узлов - минимум 3
- Синхронная репликация для критичных данных
- WAL архивирование для восстановления
- Автоматический failover механизм
- Health checks регулярная проверка
- Регулярные backups с проверкой восстановления
- Мониторинг репликации - лаг, статус
- Connection pooling с failover поддержкой
- Тестирование failover сценариев
- SLA и RTO/RPO - определи требования