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

Будет ли работать сервис при нарушении работы базы данных?

3.0 Senior🔥 131 комментариев
#REST API и микросервисы#Базы данных и SQL

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

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

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

# Будет ли работать сервис при нарушении работы базы данных?

Ответ зависит от архитектуры приложения, типа операций и наличия стратегий отказоустойчивости. В большинстве случаев сервис НЕ будет полностью работать, но степень сбоя различается.

Сценарии отказа БД

1. БД полностью недоступна

Что происходит:

Приложение → [попытка подключения] → [timeout] → Exception → Сервис падает

Типичные исключения:

org.springframework.dao.DataAccessResourceFailureException
java.sql.SQLException: Cannot get a connection
com.mysql.cj.jdbc.exceptions.CommunicationsException

Пример:

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public User getUser(Long id) {
        // Если БД не доступна
        return userRepository.findById(id).orElse(null);
        // Выбросит DataAccessException
    }
}

// В контроллере
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        try {
            User user = userService.getUser(id);
            return ResponseEntity.ok(user);
        } catch (DataAccessException e) {
            // БД недоступна
            return ResponseEntity.status(503).build();  // Service Unavailable
        }
    }
}

2. Частичная потеря доступа

Когда некоторые операции работают, а другие нет:

Операция чтения → может работать (из кэша/реплик)
Операция записи → падает (нужен мастер)
Транзакция → падает

3. Сетевая задержка и timeout

БД медленно отвечает или не отвечает совсем:

// Connection timeout
@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/db");
        config.setConnectionTimeout(10000);  // 10 сек timeout
        config.setIdleTimeout(600000);       // 10 мин idle
        config.setMaxLifetime(1800000);      // 30 мин max
        return new HikariDataSource(config);
    }
}

Как сервис реагирует

Без отказоустойчивости:

@Service
public class PaymentService {
    @Autowired
    private PaymentRepository paymentRepository;
    
    public void processPayment(Payment payment) {
        // Если БД упадёт - выбросит исключение
        paymentRepository.save(payment);
        // Весь метод падает
    }
}

// Результат: 500 Internal Server Error для клиента

С базовой обработкой ошибок:

@Service
public class PaymentService {
    @Autowired
    private PaymentRepository paymentRepository;
    
    public void processPayment(Payment payment) {
        try {
            paymentRepository.save(payment);
        } catch (DataAccessException e) {
            logger.error("Database unavailable", e);
            // Можем вернуть error response, но операция не выполнена
            throw new ServiceUnavailableException("Database is down");
        }
    }
}

Стратегии отказоустойчивости

1. Circuit Breaker Pattern

Прерываем запросы к упавшей БД, пока она восстанавливается.

@Service
public class ResilientUserService {
    @Autowired
    private UserRepository userRepository;
    
    // Hystrix / Resilience4j
    @CircuitBreaker(
        failureThreshold = 5,
        delay = 1000,
        successThreshold = 2
    )
    public Optional<User> getUser(Long id) {
        return userRepository.findById(id);
    }
    
    // Fallback - что делать если БД не доступна
    public Optional<User> getUserFallback(Long id) {
        // Вернуть из кэша или пустой результат
        return Optional.empty();
    }
}

2. Кэширование (Caching)

Если данные есть в кэше, можно вернуть их без БД.

@Service
public class CachedUserService {
    @Autowired
    private UserRepository userRepository;
    
    @Cacheable(value = "users", unless = "#result == null")
    public Optional<User> getUser(Long id) {
        return userRepository.findById(id);
    }
}

// Если БД падёт, но данные в кэше - вернём из кэша
// Если нет в кэше - вернём ошибку

3. Асинхронные операции (Async)

Записи можно отложить с помощью очереди (MessageQueue):

@Service
public class AsyncUserService {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    public void saveUserAsync(User user) {
        // Отправляем в очередь, а не в БД сразу
        rabbitTemplate.convertAndSend("user.save.queue", user);
        // Клиент получает 202 Accepted
    }
}

// Потребитель очереди
@Component
public class UserSaveConsumer {
    @Autowired
    private UserRepository userRepository;
    
    @RabbitListener(queues = "user.save.queue")
    public void processUserSave(User user) {
        try {
            userRepository.save(user);
        } catch (DataAccessException e) {
            // Повторять попытку пока БД не вернётся
            logger.error("Failed to save user, will retry", e);
            throw e;  // Вернуть в очередь
        }
    }
}

4. CQRS (Command Query Responsibility Segregation)

Разделяем чтение и запись:

// READ - может работать с репликой
@Service
public class UserQueryService {
    @Autowired
    @Qualifier("slaveDataSource")
    private JdbcTemplate readDb;
    
    public User getUser(Long id) {
        // Читаем из реплики - может быть доступна даже если мастер упал
        return readDb.queryForObject(
            "SELECT * FROM users WHERE id = ?",
            new UserRowMapper(),
            id
        );
    }
}

// WRITE - только на мастер
@Service
public class UserCommandService {
    @Autowired
    @Qualifier("masterDataSource")
    private JdbcTemplate writeDb;
    
    public void saveUser(User user) {
        // Пишем в мастер
        writeDb.update(
            "INSERT INTO users (name) VALUES (?)",
            user.getName()
        );
    }
}

5. Event Sourcing и SAGA Pattern

Данные копируются в другие хранилища (Redis, ElasticSearch):

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RedisTemplate redisCache;
    
    public void saveUser(User user) {
        try {
            // Пытаемся сохранить в основную БД
            userRepository.save(user);
        } catch (DataAccessException e) {
            // Если БД упала - сохраняем в Redis
            redisCache.opsForHash().put("users", user.getId(), user);
            // Позже синхронизируем с БД
        }
    }
}

Что работает при сбое БД

Работает:

  • Статические ресурсы (HTML, CSS, JS, изображения) - если кэшированы
  • Операции из памяти / кэша
  • Асинхронные операции, ставящие задачи в очередь
  • Чтение из реплик (если они живы)
  • Health checks показывают DOWN

НЕ работает:

  • Запросы, требующие текущих данных из БД
  • Транзакции
  • Новые записи (если нет очереди)
  • Синхронные операции

Лучшие практики

  1. Мониторь здоровье БД
@Component
public class DatabaseHealthCheck extends AbstractHealthIndicator {
    @Autowired
    private DataSource dataSource;
    
    @Override
    protected void doHealthCheck(Health.Builder builder) {
        try (Connection conn = dataSource.getConnection()) {
            conn.isValid(2);  // 2 сек timeout
            builder.up();
        } catch (Exception e) {
            builder.down()
                .withDetail("error", e.getMessage());
        }
    }
}
  1. Используй Circuit Breaker
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot3</artifactId>
</dependency>
  1. Кэшируй часто используемые данные
  2. Используй очереди для асинхронных операций
  3. Реплицируй БД для failover
  4. Логируй и мониторь все исключения доступа

Вывод

Сервис НЕ будет полностью работать при сбое БД, но:

  • С Circuit Breaker — упадёт контролируемо (503)
  • С кэшем — вернёт закэшированные данные
  • С очередями — отложит операции до восстановления
  • С репликой — сможет читать из реплики

Не полагайся на то, что сервис продолжит работать — спроектируй его так, чтобы он деградировал gracefully при сбоях.

Будет ли работать сервис при нарушении работы базы данных? | PrepBro